The personal OS that grows with you.
Built for the NousResearch "Show us what Hermes Agent can do" hackathon.
Most productivity tools forget you the moment you close them. Hermes Life OS remembers everything - your mood, your meals, your sleep, your stress, your wins and your struggles - and gets smarter about you every single day.
Tell it how you feel. Log what you ate. Track your sleep. Over time it starts connecting dots you haven't: energy crashes after poor sleep, mood dips on low-hydration days, focus drops when stress spikes. Every morning it briefs you. Every evening it reflects with you. Every week it tells you what the data says about your life.
The longer you use it, the more it knows. The more it knows, the more useful it becomes.
flowchart TD
A([👤 You share something]) --> B
B[🧠 REMEMBER<br/>Mood · Sleep · Meals<br/>Stress · Focus · Habits] --> C
C[🔍 RECALL<br/>Search memory<br/>for context] --> D
D[📊 DETECT PATTERNS<br/>Correlations across<br/>all life dimensions] --> E
E[📋 BRIEF<br/>Personalized insight<br/>based on YOUR data] --> F
F([🌱 Hermes knows you<br/>a little better today])
G([⏰ Cron Schedule<br/>07:00 Morning<br/>12:00 Midday<br/>18:00 Evening<br/>23:00 Consolidate<br/>Mon 08:00 Weekly]) --> C
style A fill:#2980b9,color:#fff
style F fill:#27ae60,color:#fff
style G fill:#8e44ad,color:#fff
style D fill:#e67e22,color:#fff
| Feature | How It's Used |
|---|---|
| Memory | Stores every mood, meal, sleep entry, workout, stress log - recalls before every response |
| Skills | Life OS playbook defines daily rhythm, pattern detection rules, and briefing format |
| Cron | Automated briefings at 07:00, 12:00, 18:00, 23:00, and weekly Monday reviews |
| Gateway | Delivers briefings via terminal - extensible to Telegram, email, SMS |
| Subagents | Pattern detection runs across all health dimensions in parallel |
| Atropos RL | Reward function trains Hermes to be more personal and memory-driven over time |
| Category | What Hermes Tracks |
|---|---|
| 🥗 Nutrition | Meals, calories, protein/carbs/fat, daily totals |
| 😴 Sleep | Duration, quality score, 7-day averages |
| 💧 Hydration | Daily water intake with progress bar |
| 💪 Fitness | Workouts, duration, intensity, weekly count |
| 🧘 Mental | Stress levels, meditation sessions, gratitude logs |
| 🎯 Focus | Deep work sessions, distractions, quality scores |
| ✅ Habits | Streaks, best streaks, completion tracking |
| 🎯 Goals | Progress percentages, milestones, notes |
| 😊 Mood & Energy | Daily scores, trend detection, dip alerts |
| 💰 Spending | Expenses by category, daily/period totals |
| 🤝 Social | Time connecting with others, quality, trend |
| ☕ Substances | Caffeine, alcohol, or anything else - amount, frequency |
| 📚 Reading | Sessions, minutes, pages, titles |
| 💊 Medication | Dose taken/skipped, adherence % by medication |
Hermes automatically detects and surfaces:
- Mood dips lasting 3+ consecutive days
- Sleep deprivation affecting focus and mood
- Energy crashes correlated with nutrition gaps
- Stress spikes and their triggers
- Habit streaks worth celebrating
- Goal stalls that need a nudge
- Hydration gaps on high-stress days
demo/analytics.py computes real Pearson correlation coefficients between
tracked metrics (mood, sleep, stress, energy, hydration) using daily-averaged
values from memory. A pair is only surfaced when there's enough data
(4+ overlapping days by default) and the relationship is meaningful
(|r| >= 0.4). Each result reports the direction (positive/negative), strength
(weak/moderate/strong), and the number of days behind it - no external
dependencies required (pure Python stdlib).
Lagged (predictive) correlations: same-day correlation can't tell you
whether poor sleep caused today's low mood, or whether being stressed
already caused last night's poor sleep - it just says the two move
together. compute_lagged_correlations() shifts one metric forward by
1-2 calendar days (correctly handling gaps from unlogged days) before
correlating, which at least points the arrow of time forward: "a higher
X on one day tends to be followed by a higher/lower Y N days later."
Both same-day and lagged results feed every surface that already shows
insights (chat replies, detect_patterns, the static and live
dashboards, the weekly email) automatically, plus a dedicated
get_correlation_insights tool for a deeper, on-demand analysis over
a custom day range - just ask "what patterns have you noticed in my
data?" or "what predicts my mood?".
pie title Life OS Reward Components
"Briefing Sent - Delivered via send_briefing?" : 30
"Memory Used - Recalled AND remembered?" : 25
"Pattern Detected - Called detect_patterns?" : 20
"Personalization - Referenced real context?" : 15
"Tool Coverage - Used expected tools?" : 10
Works with four LLM backends - pick whichever you already have. The
provider is auto-detected from whatever key is set (or force one with
--provider).
pip install "hermes-life-os[all]"
# Option A - free, fully local, no API key:
ollama serve
ollama pull llama3.1
# Option B / C / D - pick one:
set ANTHROPIC_API_KEY=sk-ant-...
set OPENAI_API_KEY=sk-...
set OPENROUTER_API_KEY=sk-or-...
hermes-life-os --mode onboard
hermes-life-os --mode morning
hermes-life-os --mode chat
# force a specific backend regardless of which keys are set:
hermes-life-os --mode morning --provider anthropicPrefer running from source instead of installing? Clone the repo and use
python demo/demo_life_os.py ... in place of hermes-life-os ... above
(same flags, same behavior) - see Project Structure.
# pull the pre-built image - no clone needed:
docker run --rm -it -e ANTHROPIC_API_KEY=sk-ant-... \
-v hermes-life-os-data:/root/.hermes \
ghcr.io/lethe044/hermes-life-os:latest --mode morningOr build it yourself, and get a fully free trial paired with a local Ollama container (no API key at all):
git clone https://github.com/Lethe044/hermes-life-os.git
cd hermes-life-os
docker compose up -d ollama
docker compose exec ollama ollama pull llama3.1
docker compose run --rm hermes-life-os --mode onboard| Mode | What Happens |
|---|---|
onboard |
First-time setup - Hermes learns who you are |
morning |
Daily briefing based on all your patterns |
checkin |
Midday log - mood, habits, quick nudge |
evening |
Evening reflection - wins, struggles, patterns |
weekly |
Sunday review - what this week says about you |
nutrition |
Log meals and get nutrition insights |
sleep |
Log sleep and get sleep analysis |
fitness |
Log workouts and track fitness patterns |
mental |
Log stress, meditation, and gratitude |
focus |
Log deep work sessions and productivity |
health |
Full health dashboard - all data in one view |
dream |
Dream journal - log dreams, detect patterns, sleep/stress correlation |
chat |
Interactive conversation - type anything |
python demo/demo_life_os.py --mode chatType naturally. Hermes responds using everything it knows about you.
Type exit to leave.
Example conversations:
- "I feel stressed today, any advice?"
- "Log my lunch - grilled chicken and rice, about 600 calories"
- "How has my sleep been this week?"
- "I just ran 5km, log it"
- "What patterns are you seeing in my data?"
- "I logged 4 hours of sleep by mistake, it was actually 7" - Hermes recalls the entry and corrects it
- "Delete that last mood entry, I misclicked" - Hermes finds and removes it (asks for confirmation first)
- "Set a goal to sleep 7+ hours a night" - Hermes tracks this automatically from your actual logged sleep, no manual progress updates needed
- "How am I doing on my goals?" / "How does this week compare to last week?"
- "Has anything been unusual lately?" / "How was I in March?"
python demo/demo_life_os.py --mode morning --profile alex
python demo/dashboard.py --profile alexBy default everything lives at ~/.hermes/life-os/ (unchanged, single
person). Passing --profile <name> (or setting LIFE_OS_PROFILE) fully
isolates that person's data under ~/.hermes/life-os/profiles/<name>/ -
so a household can share one install without mixing anyone's mood/sleep/
habit data. Omitting --profile always keeps working exactly as before.
python demo/users.py add alex --profile alex --role owner
python demo/users.py add sam --profile sam
python demo/users.py listProfiles isolate data; this registry is what turns a profile into a
person who can log in. Each user gets their own API key (shown once,
stored only as a salted PBKDF2 hash) that resolves to their own profile
automatically - so a whole household or small team can share one running
hermes-life-os-api server or one Slack bot, and everyone only ever
sees their own data:
curl -H "X-API-Key: <alex's key>" http://127.0.0.1:8765/api/health
# {"status": "ok", "profile": "alex", "user": "alex"}Fully opt-in and non-breaking - a single LIFE_OS_API_KEY/--profile
keeps working exactly as before if you never touch users.py. Full
setup, including linking a user to their Slack account, in
docs/MULTI_USER.md.
mkdir -p ~/.hermes/life-os/plugins
cp demo/plugins_examples/dice.py ~/.hermes/life-os/plugins/
python demo/demo_life_os.py --mode chat
# "roll a d20 for me"Drop a .py file defining a TOOLS list and a dispatch(name, inp)
function into ~/.hermes/life-os/plugins/, and Hermes' LLM agent can
call it like any built-in tool - next start, no core code changes. A
broken plugin is skipped and reported, never crashes the app; a plugin
can't shadow a built-in tool name. python demo/plugins.py lists
everything currently loaded. Two ready-to-copy examples ship in
demo/plugins_examples/ (a dependency-free dice/coin-flip tool, and a
profile-aware screen-time tracker showing how to persist your own data).
Full plugin API and a "share it with others" guide in
docs/PLUGINS.md.
set LIFE_OS_ENCRYPTION_KEY=your-passphrase-here
python demo/demo_life_os.py --mode morningOff by default - nothing changes unless you set this. When set, every
data file (profile, habits, goals, nutrition, sleep, etc.) and every line
of memory.jsonl is encrypted at rest with a key derived from your
passphrase (PBKDF2-HMAC-SHA256 + Fernet/AES). Existing plaintext data is
read transparently and gets encrypted the next time it's written - no
separate migration step. There is no password recovery - if you lose
the passphrase, that data is unrecoverable by design. Requires
pip install "hermes-life-os[encryption]" (or pip install cryptography
if running from source).
Changing your passphrase: use hermes-life-os-rekey rather than
just setting a new LIFE_OS_ENCRYPTION_KEY - the latter would leave
your existing files encrypted under the old key, unreadable. The
re-key tool decrypts everything with the old key, rotates the salt, and
re-encrypts everything with the new one in one step (it also works to
enable encryption for the first time, or disable it entirely):
hermes-life-os-backup # back up first
hermes-life-os-rekey --old-key "old pass" --new-key "new pass"
hermes-life-os-rekey --new-key "new pass" # enable for the first time
hermes-life-os-rekey --disable # decrypt everything back to plaintextAs of v1.38.0 re-keying covers every data store (spending, social, substance, reading, medication, achievements, templates, budgets and reminders as well as the original nine). Before that fix it only re-encrypted nine files, so after a passphrase change the others could no longer be decrypted and silently read back as empty. Backups written while a passphrase is set are encrypted too (see "Automatic Backups").
graph LR
A[hermes-life-os] --> B[skills/]
A --> C[environments/]
A --> D[demo/]
A --> E[tests/]
A --> F[docs/]
B --> B1[life-os/SKILL.md<br/>Daily rhythm playbook]
C --> C1[life_os_env.py<br/>Atropos RL environment]
C --> C2[life_os_config.yaml<br/>Training config]
D --> D1[demo_life_os.py<br/>CLI / chat / voice orchestration]
D --> D2[storage.py<br/>Persistence layer]
D --> D3[patterns.py<br/>Trend detection]
D --> D4[analytics.py<br/>Pearson correlation engine]
D --> D5[tools.py<br/>dispatch_tool + TOOLS schema]
D --> D6[scheduler.py<br/>Cron-style trigger engine]
D --> D7[notifications.py<br/>console/webhook/Telegram/email]
D --> D8[run_scheduler.py<br/>Production scheduler entry point]
D --> D9[plugins.py<br/>Community tool plugin loader]
D --> D12[life_score.py<br/>Composite 0-100 wellbeing score]
D --> D13[achievements.py<br/>Streak & milestone badges]
D --> D14[wrapped.py<br/>Shareable summary card]
D --> D15[recommendations.py<br/>Rule-based suggestion engine]
D --> D16[weather.py<br/>Open-Meteo weather correlation]
D --> D17[life_review.py<br/>Quarterly/yearly retrospective report]
D --> D18[leaderboard.py<br/>Opt-in household/team leaderboard]
D --> D19[prompts.py<br/>Deterministic daily reflection prompts]
D --> D20[moon.py<br/>Local moon phase correlation]
D --> D21[sleep_debt.py<br/>Sleep debt & bedtime suggestions]
D --> D22[heatmap.py<br/>GitHub-style SVG contribution heatmap]
D --> D23[focus_timer.py<br/>Terminal Pomodoro timer]
D --> D10[users.py<br/>Multi-user registry]
D --> D11[slack_bot.py<br/>Slack Socket Mode bot]
E --> E1[test_life_os_env.py]
E --> E2[test_analytics.py]
E --> E3[test_storage.py]
E --> E4[test_tools.py]
E --> E5[test_scheduler.py]
E --> E6[test_notifications.py]
style B1 fill:#27ae60,color:#fff
style C1 fill:#8e44ad,color:#fff
style D1 fill:#2980b9,color:#fff
style D6 fill:#e67e22,color:#fff
style D7 fill:#e67e22,color:#fff
style D9 fill:#c0392b,color:#fff
style D10 fill:#c0392b,color:#fff
style D11 fill:#c0392b,color:#fff
demo_life_os.py used to be a single ~1600-line file. It's now a thin
CLI/chat/voice orchestration layer that imports its storage, pattern
detection, and tool-dispatch logic from focused sibling modules -
each independently testable and reusable.
demo/scheduler.py implements the "Daily Rhythm" cron table from
skills/life-os/SKILL.md (07:00 morning, 12:00 midday, 18:00 evening,
Monday 08:00 weekly, 20:00 proactive nudge check) as a dependency-free
polling loop. The scheduling logic itself (due_entries) is pure and
fully unit tested; the actual briefing generation and delivery are
injected as callables, so the core engine has no dependency on the
OpenAI client or network access. The 20:00 nudge check is LLM-free
(see "Proactive Nudges" above) and stays silent when there's nothing
worth flagging.
Since v1.37.0 the scheduler loop also accepts a dynamic_source hook for
things that are not a fixed (time, mode) pair. run_scheduler.py plugs
in reminders.scheduler_source, so every timed reminder you create with
create_reminder is delivered through the same notification channel at
its saved time (see "Reminders" below), at most once per day, and a
failing source or notifier never stops the loop.
demo/notifications.py delivers briefings through a pluggable channel,
selected via HERMES_NOTIFY_CHANNEL: console (default), webhook,
telegram, or email (SMTP). All channels are stdlib-only. A failed
remote channel never crashes the scheduler - it's caught, logged, and
the briefing still prints to console.
To run the scheduler in production:
set ANTHROPIC_API_KEY=sk-ant-... # or OPENAI_API_KEY / OPENROUTER_API_KEY / a running ollama
set HERMES_NOTIFY_CHANNEL=telegram
set TELEGRAM_BOT_TOKEN=...
set TELEGRAM_CHAT_ID=...
python demo/run_scheduler.pypython demo/demo_life_os.py --voice
# or pin a backend/model explicitly:
python demo/demo_life_os.py --voice --provider anthropic --model claude-sonnet-5Speak to Hermes directly. It listens via microphone, processes your input using everything it knows about you, and responds out loud via system TTS.
No extra API key needed - uses built-in Windows/Linux speech synthesis.
To stop: say or type exit
pip install "hermes-life-os[dashboard]" # or: pip install matplotlib (running from source)
hermes-life-os-dashboard
hermes-life-os-dashboard --days 60 --compare-days 7 --out my-report.htmlTurns your logged mood/sleep/stress/energy/hydration data and the
correlations Hermes already detects (e.g. "poor sleep tracks with lower
mood, r=0.62") into a single self-contained HTML report with charts -
opens straight in your browser, no server, nothing leaves your machine.
Needs no LLM/API key at all - it's pure local data analysis. Includes a
retrospective section comparing this week to last week (--compare-days
changes the window size), color-coded by whether the change is favorable -
a stress increase shows red, a mood increase shows green.
Example output from 28 days of sample data - your own chart will reflect whatever you've actually logged.
pip install "hermes-life-os[web]" # or: pip install flask
hermes-life-os-web
# open http://127.0.0.1:8080The interactive, always-current counterpart to the static HTML report
above - same trends/correlations/retrospective/habit data, served as
JSON and rendered client-side with Chart.js, so switching the day-range
re-fetches and re-draws instantly instead of regenerating a file.
Localhost-only by default and read-only (no API key needed, unlike the
Local REST API below) - it only ever reads your own data for your own
browser. See demo/web_dashboard.py's docstring before changing
--host beyond 127.0.0.1.
Goals can track themselves from real data instead of needing manual progress updates - just tell Hermes what to track:
- "Set a goal to sleep 7+ hours a night" -> auto-tracks against your logged sleep, direction "at_least"
- "Set a goal to keep stress under 4" -> direction "at_most"
- "How am I doing on my goals?" -> recomputes and reports current progress
Progress is the average of the linked metric over a rolling window (7 days by default) relative to the target, clamped to 0-100%. Goals without a linked metric keep working exactly as before - a plain percentage you update manually.
pip install hermes-life-os
hermes-life-os-import --apple-health export.xml
hermes-life-os-import --csv my_data.csv
hermes-life-os-import --csv my_data.csv --dry-run # preview without writingReduces manual one-entry-at-a-time logging by bulk-importing data you already have, with real historical dates preserved (not stamped "today"):
- Apple Health (
export.xmlfrom Health app -> profile icon -> Export All Health Data): imports Sleep Analysis and Dietary Water records, aggregated per day. - Generic CSV: any file with a
datecolumn (YYYY-MM-DD) plus any subset ofsleep_hours, mood, stress, energy, hydrationcolumns - works for a Google Fit CSV export or your own spreadsheet.
Imported entries are tagged so they're distinguishable from entries logged live through chat.
hermes-life-os-calendar --ics calendar.ics
hermes-life-os-calendar --ics calendar.ics --dry-runCorrelates meeting-heavy days with mood/stress/sleep - no OAuth or live
API needed, just a standard .ics export (Google Calendar: Settings ->
Import & export -> Export; Outlook: File -> Save Calendar; Apple
Calendar: File -> Export). Only timed events count toward meeting hours
(all-day events are skipped); recurring events count once, on their
start date. Once imported, ask Hermes "is my stress linked to
meeting-heavy days?" or check the dashboard's Correlations section.
- "Has anything been unusual lately?" -> flags statistical outlier days (e.g. "today's stress was far above your normal range")
- "Did starting meditation on March 1st actually help?" -> compares metric averages before vs. after a specific date, instead of just a fixed weekly window
- "How was I in March?" / "Summarize last month" -> pulls real averages and notable entries (gratitude, dreams, notes) for any date range you ask about
The scheduler (see above) includes a daily check (20:00 by default) that looks for anything worth flagging - an unusual day, a goal falling behind, a monthly budget that is over or nearly used up - using the same deterministic analysis as the tools above, with no LLM call needed. It stays silent on days with nothing notable, so it won't spam you.
hermes-life-os-export --json backup.json --csv summary.csvYour data isn't locked in. --json writes a complete backup (every
memory entry plus every data store - profile, habits, goals, all the logs,
spending, budgets, reminders, templates and so on - unmodified, as readable
JSON even when encryption is on, since you asked for a copy to take
elsewhere). --csv writes
a daily summary in the same shape hermes-life-os-import --csv expects -
export, edit in a spreadsheet, and re-import elsewhere if you want.
set TELEGRAM_BOT_TOKEN=... # from @BotFather
set TELEGRAM_CHAT_ID=... # your own numeric chat id
hermes-life-os-telegramTalk to Hermes from your phone - no server, no webhook, just long
polling (keep the process running, e.g. in tmux/screen or as a
background service). Only messages from TELEGRAM_CHAT_ID are ever
processed, so your data stays private even if someone finds your bot's
username. See demo/telegram_bot.py's docstring for the exact setup
steps (getting a token and finding your chat id).
Model reliability note: this project defines a lot of tools (27+).
Small/CPU-friendly local models (e.g. llama3.2:3b) can struggle to use
them reliably - they may log things you didn't ask for, or occasionally
emit a raw tool-call attempt as plain text instead of actually calling
the tool (Hermes detects and filters that specific failure so you never
see raw JSON, but the underlying action still won't happen). llama3.1
(8B) is noticeably more reliable via Ollama, at the cost of being slower
on CPU-only machines (several minutes per reply). Any cloud provider
(Anthropic/OpenAI/OpenRouter) is both faster and more reliable if you
have API access.
pip install "hermes-life-os[discord]" # or: pip install discord.py
set DISCORD_BOT_TOKEN=... # from the Discord Developer Portal
set DISCORD_USER_ID=... # your own numeric Discord user id
hermes-life-os-discordThe Discord counterpart to the Telegram bot above - same idea (talk to
Hermes, log meals from a photo, send a voice message), different
platform. Uses discord.py's own event-driven client under the hood
(rather than the Telegram bot's hand-rolled long-polling loop), so it
works in a DM or in any server channel the bot can see. Only messages
from DISCORD_USER_ID are ever processed - everyone else is silently
ignored. See demo/discord_bot.py's docstring for the exact setup
steps (creating a bot application, enabling the Message Content
intent, and finding your user id). The same vision-model requirement
for photo meal logging applies here - see the Photo Meal Logging
section below.
pip install "hermes-life-os[whatsapp]" # or: pip install flask twilio
set TWILIO_ACCOUNT_SID=...
set TWILIO_AUTH_TOKEN=...
set WHATSAPP_ALLOWED_NUMBER=whatsapp:+1XXXXXXXXXX # your own number, E.164
hermes-life-os-whatsappThe third chat platform, via Twilio's WhatsApp API. Unlike the
Telegram bot (polling) and Discord bot (websocket client), this is a
webhook server - Twilio pushes messages to it, so it needs to be
reachable from the internet (ngrok http 8766 works well for personal
use with Twilio's free WhatsApp Sandbox). Same feature set as the
other two: plain text, photo-based meal logging, and voice-note
transcription. Every incoming request's Twilio signature is verified
before anything is processed, on top of the same single-number
authorization the other bots use. Full setup steps (sandbox join code,
webhook URL) are in demo/whatsapp_bot.py's docstring. The same
vision-model requirement for photo meal logging applies here too - see
the Photo Meal Logging section below.
pip install "hermes-life-os[slack]" # or: pip install slack_bolt
python demo/users.py add alex --profile alex
python demo/users.py link alex slack U0123ABC # your Slack member ID
set SLACK_BOT_TOKEN=xoxb-...
set SLACK_APP_TOKEN=xapp-...
hermes-life-os-slackThe fourth chat platform, and the first one built multi-user from the
start: DM the bot and it's automatically routed to your profile, so a
whole household or team can share one bot process. Uses Socket Mode (a
persistent websocket, via Slack's own slack_bolt framework) - same
"no public server, no webhook" philosophy as the Telegram bot, just
using Slack's officially supported connection handling instead of a
hand-rolled polling loop. Works single-user too (SLACK_ALLOWED_USER_ID,
same pattern as the Discord/Telegram bots) if you don't need the
multi-user registry. Same photo-based meal logging support as the other
bots. Full setup (creating the Slack app, required scopes, finding your
member ID) in demo/slack_bot.py's docstring and
docs/MULTI_USER.md.
pip install "hermes-life-os[api]" # or: pip install flask
set LIFE_OS_API_KEY=some-long-random-string
hermes-life-os-apiA lightweight, localhost-only HTTP API for third-party integrations
that don't want to (or can't) go through an LLM at all - Apple
Shortcuts, Android Tasker, a browser extension, a home-screen widget,
an Alfred/Raycast workflow, curl in a cron job, etc. Exposes the
same tools the chat agent uses:
curl -H "X-API-Key: some-long-random-string" http://127.0.0.1:8765/api/tools
curl -H "X-API-Key: some-long-random-string" -X POST \
-d '{"score": 8}' http://127.0.0.1:8765/api/tools/log_moodBinds to 127.0.0.1 by default and refuses to start without
LIFE_OS_API_KEY set - every request needs it as an X-API-Key
header. See demo/local_api.py's docstring for the full endpoint list
and the security notes on exposing this beyond your own machine. For a
step-by-step Apple Shortcuts / Android / browser bookmarklet setup, see
docs/SHORTCUTS.md.
"does the weather affect my mood?"
"any connection between rain and my energy levels?"
demo/weather.py fetches historical daily weather (temperature,
precipitation) for a place you name, via Open-Meteo's free, keyless API
(https://open-meteo.com) - no signup, no API key, no cost - and
correlates it against your tracked metrics the same way the core
correlation engine does. This is the only tracker that makes a network
call (every other one is 100% local), and it only ever does so when
explicitly asked, sending nothing but the place name you provide.
hermes-life-os-review --days 90 # a quarter -> hermes-life-review.html
hermes-life-os-review --days 365 --out my-year-review.htmlThe "big" report - ties together everything else Hermes tracks into one self-contained HTML page: your Life Score trend chart, best/toughest day, a period-over-period retrospective (this quarter vs. the one before), every correlation detected (same-day and lagged), achievements earned, and habit streaks. Same "no server, no JS build step" approach as the dashboard, just built for a much longer lookback window - a quarter or a year, rather than 30 days. Doesn't include weather correlation automatically, since that's the one feature that touches the network - ask for it separately if wanted.
Need something printable or shareable outside a browser? --format pdf renders a two-page PDF version with the same data (Life Score,
retrospective, correlations, achievements, habits) on a light,
print-friendly background:
hermes-life-os-review --days 90 --format pdf --out my-quarter.pdfhermes-life-os-wrapped supports the same trick even more simply -
just name the output file .pdf instead of .png and it's picked up
automatically:
hermes-life-os-wrapped --out hermes-wrapped.pdf"join the leaderboard"
"show me the leaderboard"
Builds on the multi-user system (see docs/MULTI_USER.md):
a friendly, opt-in ranking across every profile on the install, by
average Life Score, current logging streak, and achievements earned.
Nobody is included by default - a profile only appears after
explicitly opting in (join_leaderboard, or python demo/leaderboard.py join), and leaving again takes effect immediately. Only those three
numbers are ever shared across profiles - no journal content, no raw
logged entries, and nothing ever leaves the machine.
Habits (update_habit) now bank a streak freeze every 7 days of an
active streak, up to 3 at once - a small forgiveness mechanic so one
missed day doesn't erase weeks of consistency:
"I meditated" (7 days running) -> "Habit 'meditate': streak 7 days (best: 7) - earned a streak freeze! (1 available)"
"I missed meditating today, use a freeze" -> "Habit 'meditate': streak protected with a freeze! Still at 7 days (best: 7). 0 freeze(s) left."
No freeze banked and a day's missed? The streak resets to 0, same as always - freezes are a bonus for consistency, not a way around ever having an off day.
"what was I doing on this day last year?"
"give me a reflection prompt"
Two small additions aimed at journaling depth, not just numbers:
- On This Day (
get_on_this_day) - a nostalgia lookup: finds memory entries logged on today's month/day in previous years, most recent first. - Daily Prompt (
get_daily_prompt,demo/prompts.py) - a rotating reflection question, deterministic by calendar date (same day always returns the same prompt, and it changes daily) - no state to persist, no randomness to make testing flaky.
hermes-life-os-heatmap --days 365 --out heatmap.svg
hermes-life-os-heatmap --days 90 --out heatmap.html # wrapped with statsA GitHub-style calendar heatmap of your logging activity - darker
squares for more active days, same five-level palette as GitHub's own
contribution graph. Pure SVG (no matplotlib needed), so it's fast and
embeddable anywhere that accepts raw SVG or an <img> tag. .html
output wraps it with active-day/streak stats above the grid.
"does the full moon affect my mood?"
demo/moon.py correlates lunar phase against tracked metrics, purely
via local astronomical calculation - unlike weather.py, this makes
zero network calls; the phase is computed on-device from a known
reference new moon date. Offered in the same evidence-based spirit as
every correlation Hermes computes: not because lunar effects on mood
are scientifically established (most rigorous research says they
aren't), but because it's a fun, harmless pattern to check against your
own data - if Hermes finds nothing, that itself is the honest answer.
"how much sleep debt do I have?"
"what time should I go to bed if I'm waking up at 7?"
demo/sleep_debt.py sums the shortfall between a target sleep duration
(default 8h) and what's actually logged over a recent window - only
days with a logged sleep value count, so gaps in logging are never
mistaken for debt. get_suggested_bedtime factors existing debt into
tonight's recommended bedtime, nudged earlier gradually (a quarter of
the debt per night, capped at 1h extra) rather than proposing something
unrealistic in a single night.
"what's my best day of the week?"
"is my mood worse on Mondays?"
demo/day_of_week.py buckets already-logged mood, energy, stress,
sleep, hydration, meeting-hours, and readiness by weekday, then reports
which day averages best and worst for each metric - lower is treated
as "best" for stress specifically, since a calmer day is the good
outcome there. Purely local arithmetic over daily_averages(), no new
tracking of its own; a metric needs at least two distinct weekdays
logged before Hermes will call one "best" and another "worst".
"how close am I to a milestone on my habits?"
demo/habit_milestones.py looks at every habit with an active streak
and counts down to its next round-number milestone (7, 14, 30, 50,
100, 150, 200, 365, 500, 1000 days, doubling beyond that). This is a
different lens than the near-milestone nudge already surfaced
opportunistically by recommendations.py - get_habit_milestones
gives the full countdown list for every active habit on demand, rather
than waiting for one habit to get close enough to be worth a nudge.
"how consistent have I been with logging?"
"which trackers have I been neglecting?"
demo/consistency.py (get_logging_consistency) reports the
percentage of recent days that had at least one entry logged, overall
and broken down per tracked metric. A metric that's never been logged
in the window is simply left out of the breakdown rather than shown as
a discouraging 0% - an unused tracker isn't "inconsistent", it's just
unused.
"am I happier in the mornings?"
"is my stress worse at night?"
demo/time_of_day.py (get_time_of_day_insights) buckets individual
logged entries into Morning/Afternoon/Evening/Night by the hour they
were logged and reports which time of day is best and worst per
metric - a companion to Day-of-Week Insights that looks at time-of-day
rather than day-of-week, and works entry-by-entry rather than one
daily average, since mood at 8am and mood at 8pm on the same day
should land in different buckets.
"how is this month going compared to last month?"
demo/monthly_summary.py (get_monthly_comparison) compares this
calendar month so far against the same day-count span at the start of
last calendar month - e.g. running it on the 10th compares days 1-10
of each month, not the whole of last month against a partial current
one. Handles month-length edge cases (a 31-day January comparing
against a 28/29-day February) by capping the previous-month span at
that month's own last day. Built on top of the existing
compare_periods() averaging/delta logic, just with calendar-month
date ranges instead of rolling day windows.
"am I close to beating my meditation streak record?"
demo/habit_pb.py (get_habit_pb_progress) compares every habit's
current streak against its own personal-best streak - a new best, a
tie, or how many days remain to tie it. This is a different, often
more motivating lens than the fixed round-number countdown in Habit
Milestones: a habit whose best streak is 12 will never feel "close" to
milestone 14, but "2 days from tying your best" always does.
"give me a fitness recap"
"how's my workout streak?"
demo/workout_summary.py (get_workout_summary) totals recent
workouts, breaks them down by type, and reports the current
consecutive-day workout streak. The streak always looks at your full
workout history rather than just the requested lookback window, so a
streak that started 40 days ago still shows correctly even when asking
for a 30-day summary.
"how's my meditation practice going?"
demo/meditation_summary.py (get_meditation_summary) totals recent
meditation sessions and reports what percentage of days in the window
had at least one session - multiple sessions on the same day count
once toward that consistency figure.
"what have I been grateful for lately?"
demo/gratitude_recap.py (get_gratitude_recap) totals recent
gratitude entries, surfaces the most recurring words across them as a
lightweight signal for recurring themes (not real NLP, just frequency
counting with a small stopword list), and lists the most recent items.
"give me a nutrition recap"
demo/meal_summary.py (get_meal_summary) totals recent meals,
reports average calories per meal and per logged day, breaks meals
down by meal time (breakfast/lunch/dinner/snack), and lists the most
frequently logged foods (case-insensitively, so "Salad" and "salad"
count together).
"how well have I been hitting my water goal?"
demo/hydration_summary.py (get_hydration_summary) rebuilds daily
totals from the raw hydration log (rather than the single "today"
counter, which only reflects the current calendar day), then reports
the average glasses/day against your goal, how many logged days hit
it, and the current consecutive-day goal-met streak.
"give me a productivity recap"
demo/focus_summary.py (get_focus_summary) totals recent focus
sessions and reports total/average duration, average quality,
completion rate, total distractions, and the most common task.
"what are my recurring dream themes?"
demo/dream_recap.py (get_dream_recap) totals recent dreams and
surfaces which symbols or emotions recur across multiple distinct
dreams (a symbol mentioned twice in the same dream only counts once,
so recurrence reflects a genuine pattern across nights rather than
repetition within one dream). Dreams have no dedicated storage file of
their own - they live only in the shared memory log - so this reads
from get_recent_memory rather than a load_*() function like the
other summary tools.
"give me a stress recap"
"is my stress getting better or worse?"
demo/stress_summary.py (get_stress_summary) reports the average
stress score, a first-half-vs-second-half trend within the window
("improving", "worsening", or "steady" - a difference under 1 point on
the 0-10 scale is treated as steady, to avoid flip-flopping on small
amounts of data), how many days crossed a high-stress threshold, and
the most common triggers.
"how consistent have I really been with meditation?"
"does meditating actually improve my mood?"
"how are my habits doing overall?"
update_habit now also records every check-in (done, missed, or
protected with a freeze) as a habit_completion entry in the shared
memory log, alongside the existing streak counters in habits.json.
This unlocks three new views built on that real per-check-in history
rather than just the current streak state:
demo/habit_consistency.py(get_habit_consistency) - the percentage of check-ins that were successful for each habit, which can tell a very different story than the current streak (a short current streak can follow a long run of consistency, or vice versa).demo/habit_correlation.py(get_habit_mood_impact) - compares average mood on days a given habit was completed versus days it was missed. This is naturally forward-looking: it only reflects habit check-ins recorded after this feature shipped, and needs mood logged on both completed and missed days to say anything meaningful.demo/habit_overview.py(get_habit_overview) - a single combined dashboard over Habit Milestones, Habit Personal Bests, and Habit Consistency, for a general "how are my habits doing" question instead of picking one specific angle. Reuses each module's own formatter, so the wording matches calling that tool directly.
"how fast am I reading Atomic Habits?"
"log_reading('War and Peace', pages=40, total_pages=1225)"
log_reading now accepts an optional total_pages for a book.
demo/reading_pace.py (get_reading_pace) computes pages/day for a
specific title and, once a total page count has ever been logged for
it, estimates the remaining days and a finish date at the current
pace. Without a total_pages on file it still reports the pace, just
without an estimate - there's nothing to count down to.
"which spending categories are trending up?"
demo/spending_trends.py (get_spending_trends) compares spending
per category over a recent window against the equal-length window
before it - a different angle than the existing Spending Summary,
which only totals a single window. A category that's new or has
stopped appearing still shows up, with 0 on the missing side, rather
than silently disappearing from the comparison.
"does caffeine affect my sleep?"
demo/substance_correlation.py (get_substance_sleep_impact)
compares average sleep hours on days a given substance was logged
versus days it wasn't. This compares same-calendar-day entries rather
than "the following night", since a sleep log's date is whatever day
it was logged (typically the morning after waking) and there's no
reliable way to know which night it refers to otherwise.
"who have I been spending time with lately?"
"does socializing actually improve my mood?"
demo/social_insights.py (get_social_insights) breaks recent social
interactions down by who they were with - time spent, average quality,
and the most frequent contact - a different angle than the existing
Social Summary, which only totals the whole window.
demo/social_correlation.py (get_social_mood_impact) compares
average mood on days with a logged social interaction versus days
without one, using the same same-calendar-day comparison convention as
Habit Mood Impact and Substance-Sleep Impact.
"how consistent have I been with taking my Vitamin D?"
demo/medication_streak.py (get_medication_streak) reports the
current and longest consecutive-day streak of taking a specific
medication or supplement - a different angle than the existing
Medication Adherence, which only reports an overall percentage for the
lookback window rather than a day-by-day streak.
"does exercising actually improve my mood?"
demo/workout_correlation.py (get_workout_mood_impact) compares
average mood on days a workout was logged versus days without one,
using the same same-calendar-day comparison convention as Habit Mood
Impact, Substance-Sleep Impact, and Social Mood Impact.
"give me a fuller review of the last quarter"
Fixing wrapped.py's matplotlib crash last round turned up the exact
same bug in two more report generators: demo/dashboard.py and
demo/life_review.py both imported matplotlib at module level and
called sys.exit(1) if it wasn't installed - which would have killed
the whole bot/API process the first time a text-only caller touched
either module in an environment without matplotlib. Both now use a
shared _get_pyplot() lazy-import helper (dashboard._get_pyplot(),
reused by life_review.py) that raises a normal ImportError only
when a chart is actually about to be rendered; build_dashboard_data()
and build_life_review_data() need no plotting library at all.
That fix made demo/life_review.py's data cheap to expose
conversationally too: new format_life_review_summary() gives a text
version - life score trend, best/toughest day, correlation insights,
a retrospective comparison against the prior period, badges earned -
available as get_life_review, alongside the existing
hermes-life-os-review HTML/PDF CLI. It's the deeper counterpart to
get_wrapped's quick recap.
"is there anything I should know about?"
"give me my wrapped for the last 30 days"
Two more existing-but-CLI/scheduler-only features are now ordinary
tools: demo/nudges.py's deterministic anomaly and off-track-goal
checker (previously only run by the scheduler for proactive
notifications) is now available on demand as get_nudges, and
demo/wrapped.py's "Spotify Wrapped"-style period recap - average
life score, mood, sleep, stress, best day, badges earned - is now
available as text via get_wrapped, alongside its existing
hermes-life-os-wrapped image-card CLI.
Getting get_wrapped working safely surfaced a real bug: wrapped.py
imported matplotlib at module level and called sys.exit(1) if it
wasn't installed - fine for a CLI script, but it would have killed the
entire bot/API process the first time a text-only caller tried to use
it in an environment without matplotlib. The matplotlib import is now
lazy, living inside render_wrapped_image() (the only function that
actually needs it) and raising a normal ImportError instead of
exiting the process; build_wrapped_stats() and the new
format_wrapped_summary() need no plotting library at all.
"what stands out in my data lately?"
"does reading affect my mood?"
Habit Mood Impact, Substance-Sleep Impact, Social Mood Impact, and
Workout Mood Impact had each grown their own near-identical
"average metric X on days condition Y held versus days it didn't"
implementation. demo/correlation_utils.py now holds that logic once
compute_metric_impact_by_presence()for checks where "it didn't happen" is inferred from absence (workouts, substances, social interactions, reading), andcompute_metric_impact_by_status()for checks where "it didn't happen" needs its own explicit record (a habit logged as missed, as opposed to a habit that was never checked in on at all). Every existing correlation tool now calls one of these two functions internally; their public functions, return shapes, and wording are unchanged.
That shared foundation made two things cheap to add:
- Reading Mood Impact (
demo/reading_correlation.py,get_reading_mood_impact) - the same day-vs-day mood comparison, now for reading sessions. - Insights Digest (
demo/insights_digest.py,get_insights_digest) - runs every correlation check at once (workout/social/reading vs mood, every tracked habit vs mood, every logged substance vs sleep) and surfaces only the findings with a meaningful difference, instead of the person needing to know which of seven-plus separate tools to call, or call all of them just to see if anything stands out. A digest that repeats "not enough data" seven times isn't useful, so anything below the threshold (default 1.0 points/hours) is silently omitted.
"export my data as csv"
"back up my data right now"
Hermes Life OS has always had a full data-export system
(demo/data_export.py - JSON/CSV/Markdown) and rotating automated
backups (demo/backup.py), but until now they could only be triggered
by running those scripts directly. demo/export_tool.py wraps them as
two ordinary tools, export_data and backup_now, so they can be
triggered from a normal conversation - picking a sensible default
output path under the active profile's exports/ or backups/
directory so nobody has to supply one. export_data is for taking
your data elsewhere (a spreadsheet, Obsidian, Notion); backup_now is
for safekeeping in place.
"update_goal('meditation streak', linked_habit='meditate', target_streak=30)"
update_goal now supports a third way to track progress, alongside
manual and metric-linked: linked_habit + target_streak ties a
goal's progress directly to a habit's actual current streak (e.g. "hit
a 30-day meditation streak"), computed via a new
compute_habit_goal_progress() in analytics.py - the habit-tracking
counterpart to the existing metric-tracking compute_goal_progress().
check_goal_progress refreshes habit-linked goals from the live
streak the same way it already refreshes metric-linked ones. A goal is
manual, metric-linked, or habit-linked - not more than one at once.
"save my usual breakfast as a template"
"log my usual breakfast"
demo/templates.py lets you save a named shortcut for a specific
log_* tool call with fixed parameters (save_log_template), then
replay it later with one call (use_log_template) instead of
re-typing the same details every time - list_log_templates and
delete_log_template round it out. Templates can only ever point at
log_* tools, never at export/backup/goal/habit-management or any
other tool, so replaying one can only ever add a new log entry, never
trigger a side effect beyond what the person explicitly asked to save.
"what habits do I have set up?"
"delete my old 'read more' goal"
A real gap: a brand-new habit with a 0-day streak (created via a
missed first check-in, or one that's since dropped to a 0-day streak
with no past best) was invisible everywhere - get_habit_milestones
and get_habit_pb_progress both explicitly exclude habits with no
active or past streak, and the health dashboard only lists active
ones. list_habits now shows every habit regardless of streak status.
delete_habit and delete_goal round out basic CRUD for both -
before this, there was no way to remove a habit or goal at all once
created, only delete_entry for individual memory entries.
"set a 400/month budget for groceries"
"how am I doing on my budgets this month?"
demo/budgets.py is the project's first forward-looking money tool -
every existing spending feature (log_expense, get_spending_summary,
get_spending_trends) is retrospective, reporting what already
happened, with no concept of a limit. set_budget saves a per-category
monthly limit; get_budget_status compares it against actual spending
logged so far in the current calendar month (reusing load_spending,
no new tracking of its own) and flags any category that's over.
list_budgets shows the raw limits, and delete_budget removes one.
Setting a budget for a category that already has one overwrites the
limit rather than creating a duplicate.
"will I stay within my budgets this month?"
"how did I do on my budgets over the last 3 months?"
"where is my money going outside my budgets?"
Three more tools build on the same data (v1.37.0):
get_budget_forecastprojects month-end spending from this month's pace so far and says how much you can still spend per remaining day. Each budget isover,will_exceedoron_track, and projections made in the first days of the month are flagged as rough.get_budget_historylooks back over the last N completed months (default 3, max 24) and shows spending per month and how many months went over. Budgets are not versioned, so every month is measured against the current limit.get_unbudgeted_spendinglists this month's spending in categories that have no budget at all, biggest first.
Spending categories now match budgets case-insensitively, so an expense
logged as "Food" counts against a "food" budget (before, it was silently
missed). Budgets that are over, or at least 80% used, also show up in the
proactive nudge check and get_nudges.
v1.38.0 adds two more things:
"set a 400 budget for groceries and warn me at 90%"
"what should my budgets be?"
set_budgettakes an optionalalert_pct(1-100) so a budget can warn earlier or later than the default 80% (0 removes a custom value), and it now rejects a negative, non-numeric or infinite limit instead of storing it (a numeric string such as "400" is accepted and converted).suggest_budgetsproposes a monthly limit for every spending category that has no budget yet: the average of the last few completed months (default 3, months without spending count as zero) plus a 10% buffer (buffer_pct), rounded up to a tidy number. It only suggests; useset_budgetto adopt one.
"remind me to stretch every day at 9am"
"remind me to call mom on sundays"
"what reminders do I have set?"
demo/reminders.py gives user-defined reminders a real, queryable home
for the first time - scheduler.py's schedule was entirely hardcoded
(morning/checkin/evening/weekly/nudge_check/backup), with no mechanism
for a person to define their own. create_reminder saves free-text
reminders with an optional time hint and optional days of the week;
list_reminders and delete_reminder round out CRUD.
As of v1.37.0 reminders are actually delivered. Times are normalized to
24-hour HH:MM (9am, 9:30pm and 17:30 all work; a bare 9 is
ambiguous and rejected), and while hermes-life-os-scheduler is running
each timed reminder is sent through your notification channel at its
saved time on the days it applies to, once per day. A two-minute grace
window means a slow poll cannot skip one. A reminder with no time is a
day-level note, and one whose time cannot be understood is saved but
never fires (the agent tells you so when you create it).
"change my stretch reminder to 6:30pm"
"pause my stretch reminder while I'm away"
"what reminders do I have today?"
update_reminderchanges the text, time and/or days of an existing reminder (time=""removes the time,days=[]means every day).pause_reminder/resume_reminderswitch a reminder off and on without deleting it. Paused reminders are marked inlist_reminders.get_todays_reminderslists what applies today in time order.
v1.38.0 adds one-off and short-term reminders:
"remind me on 2026-11-02 at 9am to renew my passport"
"remind me in 20 minutes to check the oven"
"snooze that reminder for 15 minutes"
"clear my old one-off reminders"
create_reminderandupdate_reminderaccept an optionaldate(YYYY-MM-DD), making a one-off reminder that applies on that date only and ignoresdays.remind_me_insets a one-off reminder N minutes from now (1 to 1440).snooze_reminderdelays any reminder so it fires again in N minutes (default 10); its normal time is suppressed until then.clear_past_remindersdeletes one-offs whose date has passed. Recurring and upcoming reminders are never touched.
"update_goal('finish the report', deadline='2026-03-01')"
"what goal deadlines am I about to miss?"
update_goal now accepts an optional deadline (YYYY-MM-DD).
demo/goal_deadlines.py (get_goal_deadlines) lists every
deadline-bearing goal sorted by urgency, with overdue goals surfaced
first and how many days overdue they are - so a goal with a deadline
never silently falls off the radar the way a plain progress percentage
can.
hermes-life-os-focus 25 --task "writing"
hermes-life-os-focus 25 --task "deep work" --break 5A terminal Pomodoro-style countdown that logs itself on completion, via
the exact same log_focus_session path a chat-based request uses - so
this isn't a separate timer app you also have to remember to log
afterward. Ctrl+C during the countdown stops it early and does not
log a session (an abandoned session isn't a completed one).
hermes-life-os-export --markdown ./obsidian-vault/hermes
hermes-life-os-export --markdown ./notes --days 90Adds a third export format alongside JSON and CSV: one file per day
(YYYY-MM-DD.md) with YAML frontmatter for that day's numeric metrics
and a bulleted list of everything logged - filename convention matches
Obsidian's Daily Notes plugin exactly, and Notion's markdown importer
reads YAML frontmatter as page properties, so the same export folder
drops into either tool with no conversion step.
recall searches by exact keyword; ask Hermes something like "have I
felt this way before?" or "find entries about feeling overwhelmed" (even
if you never used that exact word) and it can fall back to
semantic_recall - a local, free embedding search via Ollama (ollama pull nomic-embed-text first) or OpenAI:
set EMBEDDING_PROVIDER=ollama # or openai; auto-detects from OPENAI_API_KEY otherwiseEmbeddings are cached per entry and only recomputed when that entry's text actually changes.
set OURA_PERSONAL_ACCESS_TOKEN=... # https://cloud.ouraring.com/personal-access-tokens
hermes-life-os-oura --days 30No OAuth flow - just a personal access token from Oura's own dashboard. Imports real sleep duration (merges directly with manually logged and Apple-Health-imported sleep) and your daily readiness score (a new tracked metric - ask "is my readiness linked to sleep or stress?").
set HERMES_SMTP_HOST=smtp.gmail.com
set HERMES_SMTP_PORT=587
set HERMES_SMTP_USER=you@gmail.com
set HERMES_SMTP_PASSWORD=... # an app password, not your real password
set HERMES_SMTP_TO=you@gmail.com # optional, defaults to HERMES_SMTP_USER
hermes-life-os-weekly-email
hermes-life-os-weekly-email --days 30 --compare-days 7Emails the same self-contained report hermes-life-os-dashboard
generates - trend charts, correlations, retrospective, habit streaks -
straight to your inbox. Not wired into the scheduler by default (not
everyone has SMTP configured); schedule it yourself with your OS's own
task scheduler if you want it automatic weekly:
Linux/macOS (cron): 0 8 * * 1 hermes-life-os-weekly-email
Windows (Task Scheduler): weekly trigger, action = same command
pip install "hermes-life-os[voice]" # or: pip install faster-whisperSend the Telegram bot a voice note instead of typing - it's downloaded
and transcribed locally (a free Whisper model via faster-whisper, no
cloud API, no per-minute cost) before being processed exactly like a
typed message. The reply is prefixed with what Hermes heard, so you can
catch a bad transcription. WHISPER_MODEL (default base) controls
speed vs. accuracy - tiny is fastest, small/medium are more
accurate but slower on CPU-only machines. Without faster-whisper
installed, voice notes get a clear "couldn't process" reply instead of
silently failing.
Send any of the four bots a photo of your meal (with an optional caption) and a vision-capable LLM identifies what's in it and logs it
- no separate step needed. OpenAI's and Anthropic's default models
already support vision. On Ollama, pull a vision-capable model
yourself (
ollama pull llava) and point Hermes at it explicitly (set HERMES_MODEL=llavaor--model llava) - Ollama's default text-only models (likellama3.1) will simply ignore the image.
Hermes takes a timestamped local backup of your data every day at
20:30 (right after the evening nudge check), keeping the 7 most recent
by default and pruning older ones. Backups live alongside your other
data (<profile dir>/backups/). Run it manually anytime:
hermes-life-os-backup # keep the default 7
hermes-life-os-backup --keep 14 # keep the 14 most recentA backup holds the whole memory journal plus every data store registered
in storage.DATA_STORES (18 of them), and a small _meta block with the
format version. Before v1.38.0 only nine stores were included, so the
nightly backup silently left out spending, reminders, budgets and more.
Older backups still restore fine; they just lack those sections.
If LIFE_OS_ENCRYPTION_KEY is set, backups are encrypted with it, so an
encrypted profile no longer leaves a plaintext copy of itself in
backups/. Keep the passphrase: an encrypted backup cannot be read
without it.
Through chat, backup_now takes a backup on demand and get_backup_status
reports how many exist, when the newest was taken, its size, whether it is
complete and whether it is encrypted. (backup_now always keeps at least
one backup; a keep of 0 or a nonsense value from the agent used to be
able to delete the backup it had just written.) If backups exist but the
newest is 3 or more days old, or cannot be read, the proactive nudges
say so.
hermes-life-os-restore --list # backups of this profile, newest first
hermes-life-os-restore --latest --dry-run # show exactly what would change
hermes-life-os-restore --latest # asks for confirmation first
hermes-life-os-restore backup-2026-10-01-203000.json --only spending,budgets --yes
hermes-life-os-restore backup.json --profile alexUntil v1.38.0 backups could be written but never read back. Restoring is deliberately a command-line action, not a chat tool. It is built to be hard to get wrong:
- The whole file is validated before anything is written - every section
must have the right shape and every
--onlyname must exist. One problem and nothing is touched. - Sections that are in the backup replace the current data; sections that are not in it (for example spending, when restoring an old backup) are left alone.
- A safety copy of your current data is written to
backups/pre-restore-<timestamp>.jsonfirst. These files are never rotated away, so a mistaken restore can itself be undone by restoring the safety copy. --dry-runshows the per-section before/after counts and writes nothing. Without--yesyou are asked to confirm.- Data goes through the normal storage functions, so with
LIFE_OS_ENCRYPTION_KEYset the restored files are encrypted. To restore an encrypted backup, set the same passphrase it was written with.
hermes-life-os-doctor
hermes-life-os-doctor --profile alex --json
hermes-life-os-doctor --strict # warnings also fail (exit status 1)Hermes' storage layer is deliberately forgiving: a file it cannot read
(wrong passphrase, corruption) quietly loads as empty so the app keeps
running, which also hides the problem until the data is overwritten. The
doctor is a read-only check that makes such problems visible and never
changes anything. It looks at every data store (readable, valid JSON,
right shape, consistent with the encryption key), the memory journal
(unreadable lines, duplicate ids), leftover .tmp files from an
interrupted write, backups (any at all, age of the newest, readable,
complete, older ones unreadable), reminders (a time that can never fire,
clashing ids, past one-offs) and budgets (bad limits, duplicate
categories, out-of-range alert thresholds), the day-start setting (an
invalid LIFE_OS_DAY_START_HOUR is flagged instead of silently ignored)
entries dated in the future (a sign of a wrong computer clock or a bad
import) and persistent entry ids (rows that share an id, journal entries
linked to a row that no longer exists). Exit status is 0 unless there is an error (or, with --strict, a
warning). The same report is available in chat as run_health_check.
Every tracker stamps its entries with a calendar date, and everything that
reads them back (streaks, "last N days", the budget month) has to agree on
what "today" is. Before v1.39.0 the writers used your local clock while
about a dozen readers used UTC, so for part of every day the two disagreed:
a dose logged at 00:30 in Istanbul did not count towards today's streak,
an expense logged just after midnight on the 1st landed in last month's
budget, and in UTC+14 the streak was wrong most of the day. All of them now
share one source, demo/clock.py, which is your local day.
The memory journal is deliberately different: its entries carry a UTC timestamp and the journal-based tools (life score, achievements, heatmap, correlations, "on this day") keep working in UTC.
Night owls: set LIFE_OS_DAY_START_HOUR (0-12, default 0) to the hour
your day really starts. With LIFE_OS_DAY_START_HOUR=4, something logged at
01:30 counts for the previous calendar day.
export LIFE_OS_DAY_START_HOUR=4 # Windows: set LIFE_OS_DAY_START_HOUR=4Backdating: forgot to log yesterday's lunch? The 13 log tools
(log_meal, log_sleep, log_hydration, log_workout, log_stress,
log_meditation, log_gratitude, log_focus_session, log_expense,
log_social_interaction, log_substance, log_reading,
log_medication) take an optional date (YYYY-MM-DD):
"I had a salad for lunch yesterday, about 350 calories"
"log 2 glasses of water for 2026-10-09"
Future dates, dates more than 365 days back and anything that is not a real date are rejected with a clear message and nothing is written. A backdated entry is also filed under that day in the memory journal. Backdated hydration only joins the log and leaves today's running counter alone.
Expenses were the only thing that could be corrected until v1.40.0. A meal logged with the wrong calories or a workout logged twice stayed in every average, streak and correlation. Twelve more trackers now have the same tools (meals, sleep, workouts, stress, meditation, gratitude, focus, social, substances, reading, hydration, medication; v1.41.0 added hydration, whose running "today" counter is recomputed after a change):
"show my meals from the last 3 days" -> list_logs
"that lunch was 450 calories, not 300" -> edit_log
"delete the duplicate workout" -> delete_log
"oops, undo that last log" -> undo_last_log
Every entry has a short id (shown by list_logs); edit_log takes the same
field names as the matching log tool plus date, validates them and changes
nothing if any is invalid. undo_last_log with no tracker removes whatever
was logged last (medication has no journal entry, so name it explicitly);
with a tracker name, or expenses, it removes that tracker's newest entry.
Edits and deletes also fix the memory-journal entry the log wrote, and
deleting a journal entry with delete_entry now removes the tracker row it
was linked to. Older entries get an id the first time they are listed.
get_logging_gaps lists the days with nothing logged, so they can be filled
in with the log tools' date argument. With no tracker it gives one line
per tracker you actually use; with a tracker (including hydration and
expenses) it lists every empty day. Only complete days count, today is not
over yet, and it follows your LIFE_OS_DAY_START_HOUR. log_dream accepts
date too.
"what did I forget to log this week?" -> get_logging_gaps
"I slept 7 hours on the 9th" -> log_sleep with date 2026-10-09
Four more tools make the correction workflow practical:
"what did I log yesterday?" -> get_day_log
"when did I last have pizza?" -> search_logs
"did I log anything twice this week?" -> find_duplicate_logs
"log breakfast, a 7h sleep and 3 glasses for the 10th" -> log_many
get_day_log shows everything logged on a day, grouped by tracker with ids,
ready for edit_log / delete_log. search_logs looks through food, notes,
titles, names, triggers and categories. find_duplicate_logs reports
identical entries on the same day (hydration is skipped, two identical glasses
are normal); it never changes anything. log_many logs up to 50 entries in
one call, each {tool, args} with an optional date: every shape and date is
checked first, so one bad date means nothing is logged, and a failure inside
one entry does not stop the others.
update_habit is now one check-in per day. Checking the same habit in twice
on a day no longer adds twice to the streak (and no longer writes a second
journal entry), and a day missed in between restarts the streak instead of
silently continuing it. A streak freeze still bridges a missed day. Like the
log tools it takes an optional date: a check-in for another day is recorded
in the journal, so the habit consistency analytics see it, but the running
streak counters (which describe "up to today") are left alone.
An expense used to be impossible to correct: a typo in the amount or a double entry stayed in the totals and the budgets for good. Every expense now has a short id (shown when you log it), and three tools manage them:
"show my expenses from the last week" -> list_expenses
"that lunch was 14.50, not 41.50" -> update_expense
"delete the duplicate coffee" -> delete_expense
update_expense can change the amount, category, note or date; invalid
values change nothing. Updates and deletes also fix the matching memory
journal entry, so the life score and "on this day" agree with the spending
data. Expenses saved by older versions get an id the first time you list
them.
"spent 12 on lunch"
"hung out with my best friend for an hour, really good talk"
"had 2 cups of coffee this morning"
Three more trackers alongside nutrition/sleep/fitness/mental, added because a "life OS" that only tracks the body misses a lot of what actually moves the needle day to day:
- Spending - logs expenses by category, and (like every other metric) feeds the correlation engine, so "do I spend more on stressed days?" is an answerable question, not a guess.
- Social connection - time spent with other people and how connecting/fulfilling it felt (1-10) - loneliness and social wellbeing are as real a signal as sleep or stress, just rarely tracked anywhere.
- Substances - caffeine, alcohol, or anything else worth watching, with amount and unit left free-form. Caffeine specifically feeds the correlation engine (e.g. against sleep quality); other substances are logged and summarized even if not yet wired into correlations.
Ask for a summary anytime: "how's my spending been this month?", "how much have I been socializing lately?", "how much caffeine have I had this week?".
"what's my life score today?"
"show me my achievements"
Life Score blends whatever you've logged that day - mood, sleep,
hydration, stress (inverted), energy, focus - into a single 0-100
number with a plain-language label (Thriving / Doing well / Steady /
Rough day / Tough day). Not a medical measure, just a transparent,
at-a-glance way to answer "how am I doing overall" without mentally
combining five numbers yourself - demo/life_score.py's components
field always shows exactly which metrics fed the score, so it's never
a black box, and a day with only one thing logged still scores fairly
(missing metrics are excluded, not treated as zero).
Achievements (demo/achievements.py) are streak and milestone
badges - a 7/30/100-day streak on any habit, a 7/30/100-day overall
logging streak, and count-based badges ("first workout logged", "50
mood check-ins"). Entirely read-only and recomputed fresh every time -
there's no separate achievements database to drift out of sync with
your actual logs, so editing or deleting an entry updates progress
immediately.
hermes-life-os-wrapped # last 30 days -> hermes-wrapped.png
hermes-life-os-wrapped --days 365 --out my-year.png --title "My 2026"
hermes-life-os-wrapped --days 7 # a "your week" cardA single shareable PNG card - your average Life Score, entries logged, days active, average mood/sleep, your best day, and badges earned - in the spirit of Spotify Wrapped or GitHub's yearly contribution recap. Entirely local: reads only from data already on disk, makes no LLM or network calls, and the image never leaves your machine unless you choose to share it. Needs matplotlib (already a core dependency, same as the dashboard).
"read 25 pages of Atomic Habits for 20 minutes"
"took my vitamin D"
"skipped my omega-3 today"
- Reading/learning (
log_reading,get_reading_summary) - session count, total minutes, total pages over a window. Reading minutes also feed the correlation engine. - Medication/supplement adherence (
log_medication,get_medication_adherence) - log a dose as taken or skipped, get an adherence percentage per medication over a recent window. Simple by design: no dosage/scheduling logic, no interaction warnings - just an honest log of what was actually taken.
"what should I focus on today?"
"any suggestions based on my data?"
demo/recommendations.py turns patterns already visible in your own
data into concrete, actionable nudges - entirely local, rule-based, no
LLM or network call involved, so every suggestion can be traced back to
the exact numbers behind it:
- Threshold nudges - e.g. average sleep under 6.5h or stress over 7/10 recently.
- Correlation-derived insights - reuses the same correlation engine
behind
get_correlation_insights, just phrased as a suggestion. - Near-milestone streaks - "2 days from a 30-day streak on 'meditate' - keep it going!"
Not medical or therapeutic advice - a reflection of your own patterns, phrased as a nudge, nothing more.
v1.41.0 - Habit Check-ins, Day View, Batch Logging
- Fix:
update_habitcounted every call as a new day. Checking a habit in twice on one day inflated the streak (and the freeze counter), and a gap of several days never reset it. Check-ins are now one per day, a missed day restarts the streak, andupdate_habittakes an optionaldate(recorded for the analytics, running streak untouched). - Hydration joins the entry tools (
list_logs,edit_log,delete_log,undo_last_log,delete_entrycascade): the glass log can be corrected and the running "today" counter is recomputed. - New
get_day_log(everything logged on a day, with ids),search_logs(text search across the trackers),find_duplicate_logs(identical entries on the same day, report only) andlog_many(up to 50 logs in one call, validated up front). - Doctor: the entry-id check now covers hydration rows.
- 99 new tests - suite grew from 2479 to 2578.
v1.40.0 - Fix Any Log, Undo, Catch-Up
- New
list_logs,edit_log,delete_logfor 11 trackers: until now only expenses could be corrected, so a wrong meal or a duplicate workout stayed in every average and streak. Edits are validated all-or-nothing and keep the memory-journal entry in sync. - New
undo_last_log: removes the most recent log of a tracker, or of whatever was logged last. - New
get_logging_gaps: days with nothing logged (one line per used tracker, or every empty day for one tracker), to fill in withdate. log_dreamacceptsdate. Every new log carries a persistentid, and its journal entry a matchinglog_id.delete_entryon a linked journal entry now also removes the tracker row (before it left the row behind, still counted in summaries).- Doctor: new entry-id check (duplicate ids, orphaned journal links).
- 192 new tests - suite grew from 2287 to 2479.
v1.39.0 - Local Days, Backdating, Fixing Expenses
- Fix: streaks, "last N days" windows and the budget month used UTC
while entries were stamped with local dates. Outside UTC a dose logged
"today" could miss today's streak for hours, an expense after midnight on
the 1st counted in the previous month's budget, and in UTC+14 the
medication streak read 0 most of the day. A single
demo/clock.pynow defines the user's day for all dated trackers (the UTC memory journal analytics are unchanged on purpose). - New
LIFE_OS_DAY_START_HOUR(0-12) for night owls; invalid values are flagged by the doctor. - Backdating: 13 log tools accept an optional
date, with validation (no future, at most 365 days back). - Expenses can be fixed: new
list_expenses,update_expenseanddelete_expense; expenses carry ids and stay in sync with the memory journal. - Doctor: new clock and future-dated-entries checks.
- Tests run under a pinned clock, so they cover midnight boundaries in any time zone; the suite was run under UTC, Istanbul, Kiritimati (+14), Pago Pago (-11) and Los Angeles.
- 270 new tests - suite grew from 2017 to 2287.
v1.38.0 - Data Safety: Complete Backups, Restore, Doctor
- Fix (data loss):
hermes-life-os-rekeyonly re-encrypted 9 of the 18 data files. After a passphrase change the others (spending, reminders, budgets, ...) could not be decrypted and read back as empty, and the old key was unrecoverable once the salt rotated. Re-keying now covers every store. A single registry (storage.DATA_STORES) drives backup, restore and re-keying, and a test fails if a new data file is not registered. - Fix: backups and
export_datajson now include every data store, not just nine, plus a_metablock. The nightly backup used to leave out spending, social, substance, reading, medication, achievements, templates, budgets and reminders. - New
hermes-life-os-restore: restores a backup with up-front validation,--dry-run,--only,--list/--latest, a never-rotatedpre-restore-*safety copy and a confirmation prompt. Old backups restore without wiping the sections they lack. - Backups are encrypted when
LIFE_OS_ENCRYPTION_KEYis set (before, an encrypted profile left plaintext backups behind). - New
hermes-life-os-doctorandrun_health_check: a read-only check of stores, the memory journal, leftover temp files, backups, reminders and budgets. - New
get_backup_statustool, and a proactive nudge when the newest backup is 3 or more days old or unreadable. - Fix:
backup_nowcould delete every backup, including the one it had just written, ifkeepwas 0 or invalid. It now always keeps at least one. - Reminders: one-off dated reminders,
remind_me_in,snooze_reminder,clear_past_reminders. - Budgets: per-budget
alert_pct, input validation forset_budget, andsuggest_budgets. - Fix (tests):
test_with_data_surfaces_findingfailed every night between 00:00 and the UTC offset (it mixed local and UTC dates); the suite now passes in UTC+14, UTC-11 and UTC-8 as well as UTC and UTC+3. - 512 new tests - suite grew from 1505 to 2017.
v1.37.0 - Live Reminders & Budget Forecasting
- Reminders are delivered.
scheduler.run_scheduler()gained a backwards-compatibledynamic_sourcehook andrun_scheduler.pywiresreminders.scheduler_sourceinto it, so timed reminders fire through the configured notification channel (once per day, with a small grace window; a broken source or notifier never stops the loop). - New reminder tools:
update_reminder,pause_reminder,resume_reminder,get_todays_reminders. Times are normalized to 24-hourHH:MM(9am,9:30pm), andcreate_remindernow tells the user when a time cannot be scheduled. - New budget tools:
get_budget_forecast(month-end projection and per-day allowance),get_budget_history(last N completed months) andget_unbudgeted_spending(spending outside any budget). - Over-budget and nearly-used-up budgets now appear in the proactive
nudge check and
get_nudges. - Fix: spending categories now match budgets case-insensitively. An expense logged as "Food" was previously not counted against a "food" budget.
- 151 new tests - suite grew from 1354 to 1505.
v1.36.0 - Budgets & Reminders
- New Budget Layer (
demo/budgets.py,set_budget/get_budget_status/list_budgets/delete_budget): the project's first forward-looking money tool, alongside the existing retrospective spending features. A per-category monthly limit is compared against real spending logged so far this calendar month (viaload_spending), flagging categories that are over budget. - New Reminders Layer (
demo/reminders.py,create_reminder/list_reminders/delete_reminder): user-defined reminders with an optional time hint and days of the week, finally giving a person a way to define their own reminder beyondscheduler.py's hardcoded schedule. Scoped to storage/CRUD only in this round - not yet wired intorun_scheduler()or live notification delivery. - 51 new tests - suite grew from 1303 to 1354.
v1.35.0 - List, Delete Habits & Goals
- New
list_habits: shows every habit regardless of streak status- fills a real blind spot where a brand-new or fully-reset habit
(0-day streak, no past best) was invisible to
get_habit_milestones,get_habit_pb_progress, and the health dashboard, all of which only cover habits with an active or past streak.
- fills a real blind spot where a brand-new or fully-reset habit
(0-day streak, no past best) was invisible to
- New
delete_habitanddelete_goal: basic CRUD completion - previously there was no way to remove a habit or goal at all once created, onlydelete_entryfor individual memory entries. - 10 new tests - suite grew from 1293 to 1303.
v1.34.0 - Habit-Linked Goals & Quick-Log Templates
- New Habit-Linked Goals:
update_goalnow acceptslinked_habittarget_streakas a third progress-tracking mode alongside manual and metric-linked - a goal like "hit a 30-day meditation streak" now auto-tracks itself from the habit's real current streak via newcompute_habit_goal_progress().check_goal_progressrefreshes these the same way it already refreshes metric-linked goals.
- New Quick-Log Templates (
demo/templates.py,save_log_template/use_log_template/list_log_templates/delete_log_template): save a named shortcut for a specificlog_*tool call with fixed parameters and replay it later with one call. Restricted tolog_*tools only, so a template can never trigger anything beyond adding a new log entry. - 36 new tests - suite grew from 1257 to 1293.
v1.33.0 - Life Review Conversational, Two More Matplotlib Crash Fixes
- Bug fix: found the same matplotlib-crash bug fixed in
wrapped.pylast release in two more places -demo/dashboard.pyanddemo/life_review.pyboth imported matplotlib at module level and calledsys.exit(1)if it was missing. Both now use a shared, lazily-evaluated_get_pyplot()helper that raises a normalImportErroronly when a chart is actually being rendered.build_dashboard_data()andbuild_life_review_data()need no plotting library at all now; the CLIs' behavior is unchanged. - New
get_life_review: a text version ofdemo/life_review.py's full period review (life score trend, best/toughest day, correlation insights, retrospective comparison, badges earned), via a newformat_life_review_summary()- the deeper counterpart toget_wrapped's quick recap. - 9 new tests - suite grew from 1248 to 1257.
v1.32.0 - Proactive Nudges & Wrapped, Now Conversational
- New
get_nudges: exposes the scheduler's existing deterministic anomaly and off-track-goal checker (demo/nudges.py) on demand, instead of only running on a schedule. - New
get_wrapped: a text version ofdemo/wrapped.py's "Spotify Wrapped"-style period recap, via a newformat_wrapped_summary()alongside the existing image-renderingrender_wrapped_image(). - Bug fix:
wrapped.pyused to import matplotlib at module level and callsys.exit(1)if it was missing - harmless for a CLI script, but it would have crashed the whole bot/API process the first timeget_wrappedran anywhere without matplotlib installed. The import is now lazy (insiderender_wrapped_image()only) and raises a normalImportError; the CLI'smain()still exits cleanly with the same message as before. - 10 new tests - suite grew from 1238 to 1248.
v1.31.0 - Correlation Refactor, Reading Mood Impact, Insights Digest
- Refactored Habit Mood Impact, Substance-Sleep Impact, Social
Mood Impact, and Workout Mood Impact to share one implementation in
new
demo/correlation_utils.py, instead of four nearly-identical same-calendar-day comparison implementations. Public functions, return shapes, and wording are all unchanged - verified by the full existing test suite for all four passing unmodified. - New Reading Mood Impact (
demo/reading_correlation.py,get_reading_mood_impact): the same day-vs-day mood comparison, for reading sessions - made cheap to add by the refactor above. - New Insights Digest (
demo/insights_digest.py,get_insights_digest): runs every correlation check at once and surfaces only the findings with a meaningful difference, instead of needing to call seven-plus separate correlation tools to find out if anything stands out. - 25 new tests - suite grew from 1213 to 1238.
v1.30.0 - Workout Mood Impact, Conversational Export & Backup
- New Workout Mood Impact (
demo/workout_correlation.py,get_workout_mood_impact): compares average mood on workout days versus non-workout days. - New Conversational Export & Backup (
demo/export_tool.py,export_dataandbackup_now): exposes the existingdata_export.py(JSON/CSV/Markdown) andbackup.py(rotating backups) CLI scripts as ordinary dispatch_tool calls, so they can be triggered from chat instead of only from the command line. Picks a sensible default output path under the active profile'sexports/orbackups/directory. - README badge fix: swapped the PyPI Downloads badge from
shields.io's
pypi/dm(which depends on pypistats.org and intermittently shows "rate limited by upstream service") to pepy.tech's own badge service. - 22 new tests - suite grew from 1191 to 1213.
v1.29.0 - Social Insights, Social Mood Impact, Medication Streak
- New Social Insights (
demo/social_insights.py,get_social_insights): recent social interactions broken down by who they were with - time spent, average quality, and the most frequent contact. - New Social Mood Impact (
demo/social_correlation.py,get_social_mood_impact): compares average mood on days with a logged social interaction versus days without one. - New Medication Streak (
demo/medication_streak.py,get_medication_streak): current and longest consecutive-day streak of taking a specific medication/supplement, complementing the existing overall-percentage Medication Adherence. - Also refreshed the README badge row: added PyPI downloads, dynamic Python-versions (from PyPI metadata instead of a static "3.10+"), GitHub stars, and last-commit badges alongside the existing Tests/PyPI-version/License badges.
- 28 new tests - suite grew from 1163 to 1191.
v1.28.0 - Reading Pace, Spending Trends, Substance-Sleep Impact
- Enhanced
log_reading: now accepts an optionaltotal_pagesfor a book, enabling a real finish estimate. - New Reading Pace (
demo/reading_pace.py,get_reading_pace): pages/day for a specific book, and an estimated finish date once a total page count has been logged for it. - New Spending Trends (
demo/spending_trends.py,get_spending_trends): compares spending per category between a recent window and the equal-length window before it - which categories are trending up or down, complementing the existing single-window Spending Summary. - New Substance-Sleep Impact (
demo/substance_correlation.py,get_substance_sleep_impact): compares average sleep hours on days a substance was logged versus days it wasn't. - 39 new tests - suite grew from 1124 to 1163.
v1.27.0 - Habit Consistency, Mood Impact, and Overview
- Enhanced
update_habit: every check-in (done, missed, or protected with a freeze) is now also recorded as ahabit_completionentry in the shared memory log, giving Hermes a real per-check-in history to analyze instead of only the current streak counters inhabits.json. - New Habit Consistency (
demo/habit_consistency.py,get_habit_consistency): percentage of successful check-ins per habit, a different and sometimes more honest signal than the current streak alone. - New Habit Mood Impact (
demo/habit_correlation.py,get_habit_mood_impact): compares average mood on days a habit was completed versus missed - a genuine correlation feature, naturally forward-looking since it depends on check-in history recorded from here on. - New Habit Overview (
demo/habit_overview.py,get_habit_overview): a single combined dashboard over Habit Milestones, Habit Personal Bests, and Habit Consistency for a general "how are my habits doing" question. - 33 new tests - suite grew from 1091 to 1124.
v1.26.0 - Hydration, Focus, Dream, and Stress Summaries
- New Hydration Summary (
demo/hydration_summary.py,get_hydration_summary): average daily intake vs. goal, days the goal was met, and the current consecutive-day goal-met streak, rebuilt from the raw hydration log rather than the single "today" counter. - New Focus Summary (
demo/focus_summary.py,get_focus_summary): totals, average duration/quality, completion rate, total distractions, and the most common task. - New Dream Recap (
demo/dream_recap.py,get_dream_recap): totals, average vividness, most common tone, and symbols/emotions that recur across multiple distinct dreams. Reads fromget_recent_memorysince dreams have no dedicated storage file. - New Stress Summary (
demo/stress_summary.py,get_stress_summary): average score, a first-half-vs-second-half trend within the window, high-stress day count, and top triggers. - 43 new tests - suite grew from 1048 to 1091.
v1.25.0 - Workout, Meditation, Gratitude, and Meal Summaries
- New Workout Summary (
demo/workout_summary.py,get_workout_summary): totals, breakdown by workout type, and a consecutive-day workout streak computed over full history rather than just the requested window. - New Meditation Summary (
demo/meditation_summary.py,get_meditation_summary): totals and a consistency percentage (days with at least one session logged). - New Gratitude Recap (
demo/gratitude_recap.py,get_gratitude_recap): totals, recurring-word frequency across logged items, and the most recent entries. - New Meal Summary (
demo/meal_summary.py,get_meal_summary): totals, average calories per meal/day, breakdown by meal time, and the most frequently logged foods. - 43 new tests - suite grew from 1005 to 1048.
v1.24.0 - Logging Consistency, Time-of-Day Insights, Month-over-Month, Habit PBs
- New Logging Consistency (
demo/consistency.py,get_logging_consistency): percentage of recent days with at least one entry logged, overall and per tracked metric - unused trackers are omitted rather than shown as a discouraging 0%. - New Time-of-Day Insights (
demo/time_of_day.py,get_time_of_day_insights): buckets individual logged entries into Morning/Afternoon/Evening/Night by hour and reports the best/worst time of day per metric - a companion to Day-of-Week Insights that works entry-by-entry instead of on daily averages. - New Month-over-Month Comparison (
demo/monthly_summary.py,get_monthly_comparison): compares this calendar month so far against the same day-count span at the start of last calendar month, reusingcompare_periods()'s averaging/delta math with calendar month boundaries instead of rolling day windows; correctly caps a longer current month's span against a shorter previous month (e.g. comparing a 31-day January against February). - New Habit Personal Bests (
demo/habit_pb.py,get_habit_pb_progress): compares every habit's current streak against its own personal best - new best, tied, or days remaining to tie it - a complement to the fixed-milestone countdown in Habit Milestones. - Also promotes
analytics._extract_metricto a publicextract_metric(backward-compatible, original kept) sotime_of_day.pycould reuse the exact same per-entry metric extraction rather than duplicating the per-type rules. - 54 new tests - suite grew from 951 to 1005.
v1.23.0 - Day-of-Week Insights, Habit Milestones, Goal Deadlines
- New Day-of-Week Insights (
demo/day_of_week.py,get_day_of_week_insights): buckets logged mood/energy/stress/sleep/ hydration/meeting-hours/readiness by weekday and reports which day is best and worst for each metric, purely from data already logged - no new tracking, no network call. - New Habit Milestones (
demo/habit_milestones.py,get_habit_milestones): counts down every active habit streak to its next round-number milestone (7, 14, 30, 50, 100, 150, 200, 365, 500, 1000 days) in one on-demand list, complementing the existing opportunistic near-milestone nudge inrecommendations.py. - New Goal Deadlines (
demo/goal_deadlines.py,get_goal_deadlines):update_goalnow accepts an optionaldeadline(YYYY-MM-DD);get_goal_deadlineslists every deadline-bearing goal sorted by urgency, overdue ones called out first with how many days overdue. - 42 new tests - suite grew from 909 to 951.
v1.22.0 - Heatmap, Moon Correlation, Sleep Debt, Focus Timer, Markdown Export
- New Contribution Heatmap (
demo/heatmap.py,hermes-life-os-heatmap): a GitHub-style SVG calendar heatmap of logging activity - pure SVG, no matplotlib, embeddable anywhere. - New Moon Phase Correlation (
demo/moon.py,get_moon_correlation): correlates lunar phase against tracked metrics via pure local astronomical calculation - zero network calls, unlike weather.py. - New Sleep Debt Calculator (
demo/sleep_debt.py,get_sleep_debt,get_suggested_bedtime): cumulative shortfall against a target sleep duration, plus a bedtime suggestion that pays down debt gradually. - New Focus Timer (
demo/focus_timer.py,hermes-life-os-focus): a terminal Pomodoro-style countdown that logs itself on completion via the same path a chat-based focus session uses. - New Markdown/Obsidian/Notion Export (
data_export.py --markdown): one daily note per day with YAML frontmatter, matching Obsidian's Daily Notes convention and readable by Notion's markdown importer. - Also promotes
achievements._consecutive_day_streakto a publicconsecutive_day_streak(backward-compatible alias kept) so heatmap.py could reuse the exact same streak logic rather than duplicating it. - 75 new tests - suite grew from 834 to 909.
v1.21.0 - PDF Export, Streak Freezes, On This Day, Daily Prompts
- PDF export for the two biggest reports:
hermes-life-os-review --format pdfrenders a two-page, print-friendly PDF version of the Life Review (Life Score, retrospective, correlations, achievements, habits);hermes-life-os-wrapped --out card.pdfpicks up PDF automatically from the file extension - no new flags needed. - Streak freezes: habits now bank one streak freeze every 7 days of
an active streak (capped at 3) -
update_habitwithcompleted=false, use_freeze=truespends one to protect a streak through a missed day instead of resetting it to 0. - New On This Day (
get_on_this_day): a nostalgia lookup - finds memory entries logged on today's month/day in previous years. - New Daily Prompt (
get_daily_prompt,demo/prompts.py): a rotating reflection question, deterministic by calendar date - same day always returns the same prompt, changes daily, no state to persist. - Also fixes a real Python variable-scoping bug found during testing:
a local
from storage import get_all_memoryinside onedispatch_toolbranch was shadowing the module-level import for the entire function, breaking two unrelated tools (get_correlation_insights's semantic-recall path andcompare_periods) wheneverget_on_this_dayhad been added to the same file. Caught by the full test suite before release, not after. - 62 new tests - suite grew from 806 to 834.
v1.20.0 - Life Review Report, Household Leaderboard
- New Life Review (
demo/life_review.py,hermes-life-os-review): the flagship retrospective report - Life Score trend chart, best/toughest day, a period-over-period comparison, every correlation detected, achievements earned, and habit streaks, all in one self-contained HTML page built for a quarter- or year-long lookback window. Reuses the same chart-rendering approach as the dashboard for visual consistency. - New Leaderboard (
demo/leaderboard.py,join_leaderboard/leave_leaderboard/get_leaderboardtools): an opt-in, cross-profile ranking by average Life Score, logging streak, and achievements earned, built on top of the multi-user system. Nobody is included by default; opting out takes effect immediately; only those three numbers are ever shared across profiles. - 33 new tests - suite grew from 768 to 805.
v1.19.0 - Weather Correlation, Quick-Logging Guide
- New Weather Correlation (
demo/weather.py,get_weather_correlationtool): fetches historical daily weather via Open-Meteo's free, keyless API and correlates temperature/precipitation against tracked metrics using the same Pearson approach as the core correlation engine. The only tracker that makes a network call - entirely on-demand, sends nothing but the place name. - New docs/SHORTCUTS.md: a step-by-step guide for building one-tap Apple Shortcuts, Android (Tasker/HTTP Shortcuts), and browser-bookmarklet quick-loggers on top of the existing local REST API - no new app, no subscription.
- 20 new tests (all HTTP calls mocked - no real network access required to run the suite) - suite grew from 748 to 768.
v1.18.0 - Reading & Medication Tracking, Recommendations
- New trackers: reading/learning (
log_reading,get_reading_summary- sessions, minutes, pages; reading minutes feed the correlation engine) and medication/supplement adherence (log_medication,get_medication_adherence- taken/skipped dose logging with an adherence % per medication). - New Recommendations (
demo/recommendations.py,get_recommendationstool): a fully local, rule-based suggestion engine combining threshold nudges (low sleep, high stress), correlation-derived insights (reusing the existing correlation engine), and near-milestone habit streaks into concrete, traceable suggestions - no LLM or network call involved. - 26 new tests - suite grew from 722 to 748.
v1.17.0 - Spending/Social/Substance Tracking, Life Score, Achievements, Wrapped
- New trackers alongside nutrition/sleep/fitness/mental: spending
(
log_expense,get_spending_summary), social connection (log_social_interaction,get_social_summary), and substances (log_substance,get_substance_summary- caffeine, alcohol, or anything else). Spending and caffeine feed the correlation engine like every other metric. - New Life Score (
demo/life_score.py,get_life_scoretool): a single 0-100 composite blending whatever's logged that day (mood, sleep, hydration, stress, energy, focus) into one at-a-glance wellbeing number, with a transparentcomponentsbreakdown - never a black box, and never penalized for partial data. - New Achievements (
demo/achievements.py,get_achievementstool): streak badges (7/30/100 days, per-habit and overall) and count-based milestone badges. Fully read-only and recomputed fresh every call - no separate achievements state to fall out of sync with your actual logs. - New Wrapped (
demo/wrapped.py,hermes-life-os-wrapped): a single shareable PNG summary card (average Life Score, entries logged, best day, badges earned) in the spirit of Spotify Wrapped - entirely local, no network calls. - 81 new tests - suite grew from 641 to 722.
v1.16.0 - Plugin System, Multi-User Accounts, Slack Bot
- New plugin system (
demo/plugins.py): drop a.pyfile into~/.hermes/life-os/plugins/definingTOOLS+dispatch()and Hermes' LLM agent can call it like any built-in tool - no fork, no core code changes. A broken plugin is skipped and reported, never crashes startup; plugins can't shadow built-in tool names. Ships with two ready-to-copy examples (demo/plugins_examples/) and a full guide at docs/PLUGINS.md. Also fixes a long-standing dead-code bug wheredispatch_tool's final "Unknown tool" fallback could never actually be reached. - New multi-user registry (
demo/users.py): named users, each with their own salted-hash API key that resolves to their own profile automatically.hermes-life-os-apiand the new Slack bot are both multi-user aware - one running server/bot can now serve a whole household or team, each person only ever seeing their own data. Fully opt-in; a singleLIFE_OS_API_KEY/--profilekeeps working exactly as before. See docs/MULTI_USER.md. - New Slack bot (
demo/slack_bot.py,hermes-life-os-slack): the fourth chat platform, via Slack's Socket Mode (no public server/ webhook needed, same as the Telegram bot). Supports both single-user (SLACK_ALLOWED_USER_ID) and multi-user (linked viausers.py) modes, plus the same photo-based meal logging as the other bots. - 74 new tests - suite grew from 567 to 641.
v1.15.0 - Local REST API, Live Web Dashboard, WhatsApp Bot, Predictive Correlations
- New
hermes-life-os-apilocal REST API (demo/local_api.py, Flask):GET /api/tools,POST /api/tools/<name>,GET /api/memory/recent,GET /api/memory/search- lets a Shortcut, browser extension, or any other client call Hermes' tools over HTTP. Binds to localhost by default and requiresLIFE_OS_API_KEYon every request; refuses to start without it. - New live web dashboard (
demo/web_dashboard.py,hermes-life-os-web) - the same charts/correlations/retrospective as the static PNG report, but as an always-current local web page that refreshes without regenerating a file. - New WhatsApp bot (
demo/whatsapp_bot.py,hermes-life-os-whatsapp), via Twilio's WhatsApp API - the third chat platform alongside Telegram and Discord, with the same text/photo-meal-logging feature set. Unlike the polling/websocket-based bots, this is a webhook server, so every incoming request's Twilio signature is verified before processing. - New lagged/predictive correlations (
compute_lagged_correlations()indemo/analytics.py): shifts one metric 1-2 days forward before correlating, so results can say "a higher X on one day tends to be followed by a higher/lower Y N days later" instead of only reporting same-day co-movement. Feeds every existing insight surface (chat,detect_patterns, both dashboards, the weekly email) plus a new dedicatedget_correlation_insightstool for on-demand deep dives.
v1.14.0 - Automatic Backups, Photo Meal Logging, Discord Bot, Encryption Re-key
- Automatic daily local backups of all data, kept on a rolling window,
restorable via
hermes-life-os-backup. - Photo-based meal logging: send the Telegram or Discord bot a photo of a meal (with an optional caption) and a vision-capable LLM identifies and logs it - no separate step needed.
- New Discord bot (
demo/discord_bot.py,hermes-life-os-discord) - the second chat platform alongside Telegram, using discord.py's event-driven client rather than a hand-rolled polling loop, so it works in a DM or any server channel the bot can see. - New
hermes-life-os-rekeytool: safely rotatesLIFE_OS_ENCRYPTION_KEY(decrypts with the old key, rotates the salt, re-encrypts with the new one in one step) - also works to enable or fully disable encryption after the fact, which simply setting a new key directly could not do safely.
v1.13.0 - Weekly Email Summary, Voice Notes
- New
hermes-life-os-weekly-emailCLI: emails the same self-contained dashboard report (charts, correlations, retrospective, habits) thathermes-life-os-dashboardgenerates. Reuses the existing SMTP settings (HERMES_SMTP_*); newnotifications.send_html_email()sends HTML with a plain-text fallback. Not wired into the scheduler by default - schedule it yourself with cron/Task Scheduler if wanted. - Telegram bot now accepts voice notes: downloaded and transcribed
locally via a free Whisper model (
faster-whisper, no cloud API,WHISPER_MODELenv var to pick model size), then processed exactly like a typed message. The reply is prefixed with what Hermes heard, and a missing/failed transcription gets a clear message instead of silently failing. - 36 new tests - suite grew from 355 to 391.
v1.12.0 - Telegram Bot, Semantic Memory Search, Oura Ring Import
- New
hermes-life-os-telegramCLI: talk to Hermes from your phone via long-polling (no server/webhook needed). Restricted to a singleTELEGRAM_CHAT_IDfor privacy. Replies userun_life_os()'s newreply_textfield - the model's actual natural-language answer, extracted directly rather than parsed from rendered terminal output (avoids garbled box-drawing characters and empty-panel replies). Long messages auto-split across Telegram's 4096-char limit; failed polls back off exponentially (5s -> 5min) instead of hammering the API if the token is briefly rate-limited or wrong. - Hardening: if a weak/small model emits a raw failed tool-call attempt
as plain text (e.g.
{"name":"recall","parameters":{...}}) instead of actually calling the tool, that's now detected and never relayed to the user as if it were a real answer - seen with small local models (e.g.llama3.2:3b) via Ollama, which can also make unreliable tool choices in general;llama3.1(8B) or a cloud provider is recommended for more consistent behavior. - New
semantic_recallchat tool +semantic_search.py: meaning-based memory search via local (Ollama) or OpenAI embeddings, with a per-entry cache that only recomputes when an entry's text actually changes. Falls back gracefully with a clear message if no embedding provider is reachable. - New
hermes-life-os-ouraCLI: imports real sleep duration (merges directly into the existing "sleep" metric alongside manual logs and Apple Health imports) and daily readiness score (readiness, a new fully tracked metric) from an Oura Ring, via Personal Access Token - no OAuth flow needed. - 83 new tests - suite grew from 272 to 355.
v1.11.0 - Anomaly Detection, Calendar Import, Proactive Nudges, Data Export, History Queries
check_anomaliestool +analytics.detect_anomalies(): flags statistical outlier days (z-score based) in mood/energy/stress/sleep/hydration.compare_before_aftertool +analytics.compare_before_after(): compares metric averages before vs. after a specific changepoint date (e.g. "did starting meditation on March 1st actually help?").- New
hermes-life-os-calendarCLI: imports meeting hours per day from a standard.icscalendar export (Google Calendar/Outlook/Apple Calendar, no OAuth needed).meeting_hoursis now a fully tracked metric - participates in correlations, goal-linking, retrospectives, and anomaly detection automatically. - Proactive nudges: the scheduler's new 20:00
nudge_checkentry deterministically (no LLM call) surfaces anomalies and lagging metric-linked goals, staying silent when nothing stands out. - New
hermes-life-os-exportCLI:--jsonfor a complete backup,--csvfor a daily summary in the same shapehermes-life-os-import --csvexpects (export, edit, re-import). get_period_summarytool +storage.get_memory_by_date_range(): natural- language history queries like "how was I in March?" - the LLM resolves the phrase to concrete dates, Hermes returns real averages and notable entries for that period.- 52 new tests - suite grew from 220 to 272.
v1.10.0 - Goal-Metric Linkage, Retrospective Comparison, Health Data Import
- Goals can now auto-track from real logged data instead of manual progress
updates -
update_goalacceptsmetric/target/direction/window_days; newcheck_goal_progresstool recomputes and reports current progress. - New
compare_periodstool and a Dashboard "Retrospective" section compare this period to the one before it (week-over-week by default,--compare-daysto change the window), color-coded by whether the change is favorable per metric. - New
hermes-life-os-importCLI: bulk-imports Apple Healthexport.xml(Sleep Analysis, Dietary Water) or a generic CSV (date + sleep_hours/mood/stress/energy/hydration columns), preserving real historical dates instead of stamping everything "today". write_memory()now only stamps "now" when no timestamp was already provided - unchanged for all real-time logging (which never supplies one), enables historical-dated bulk import.- 45 new tests (goal-metric linkage, retrospective comparison, health import) - suite grew from 175 to 220.
v1.9.0 - Multi-Profile, Encryption at Rest, Correcting/Deleting Entries
--profile <name>(orLIFE_OS_PROFILE) isolates all data per person under~/.hermes/life-os/profiles/<name>/- for shared households. Omitting it keeps the original single-profile layout unchanged.LIFE_OS_ENCRYPTION_KEY- optional encryption at rest (PBKDF2-HMAC-SHA256- Fernet/AES) for every data file and every memory.jsonl line. Off by default; existing plaintext data reads transparently and gets encrypted on next write, no separate migration needed.
- Every memory entry now has a stable id. New
correct_entry/delete_entrytools let you fix a mistake or remove a bad log entry through normal conversation ("that sleep entry was wrong, it was actually 7 hours" / "delete that last entry") instead of it being stuck in an append-only log. - 39 new tests (12 profiles, 10 encryption, 12 memory edit/delete at the storage layer, 5 for the correct_entry/delete_entry chat tools) - suite grew from 136 to 175 tests.
v1.8.0 - PyPI Package, GHCR Image, Contributor Docs
pip install hermes-life-os- real PyPI packaging viapyproject.toml, with CLI commandshermes-life-os,hermes-life-os-dashboard,hermes-life-os-scheduler. Source layout (demo/) unchanged, so existingpython demo/demo_life_os.pyusage still works exactly the same. Auto-published to PyPI on every GitHub Release.ghcr.io/lethe044/hermes-life-os- pre-built Docker image, auto-published on every push tomainand every release. Nogit cloneneeded to try it.- Example dashboard chart embedded in the README (see the Dashboard section).
CONTRIBUTING.mdand GitHub issue templates (bug report / feature request) for contributors.
v1.7.0 - CI, Docker & Dashboard
- GitHub Actions workflow runs the full test suite on every push/PR across Python 3.10/3.11/3.12, with a status badge in this README
Dockerfile+docker-compose.ymlfor a zero-install trial - pairs with a local Ollama container for a completely free, no-API-key run- New
demo/dashboard.py: generates a self-contained HTML report with charts of your mood/sleep/stress/energy/hydration trends and the correlations Hermes detects - pure local data analysis, no LLM call - 7 new tests for the dashboard - suite grew from 129 to 136 tests
v1.6.0 - Multi-Provider LLM Support
- New
demo/llm_providers.py: provider-agnostic client layer supporting Ollama (free, fully local, no API key), OpenAI, Anthropic, and OpenRouter, with auto-detection from whichever key is set --providerflag /LIFE_OS_PROVIDERenv var to force a specific backend- Friendly troubleshooting output on connection/auth failures instead of raw tracebacks
- 16 new unit tests (
test_llm_providers.py) covering provider resolution and the Anthropic <-> OpenAI message/tool format adapter - total suite grew from 113 to 129 tests, all passing
v1.5.0 - Modular Architecture, Scheduler & Notifications
- Split the ~1600-line
demo_life_os.pymonolith into focused, independently testable modules:storage.py,patterns.py,tools.py(demo_life_os.py is now the CLI/chat/voice orchestration layer only) - New
demo/scheduler.py: dependency-free cron-style engine implementing the Daily Rhythm table (07:00 morning, 12:00 midday, 18:00 evening, Monday 08:00 weekly), with pure, fully unit-tested scheduling logic - New
demo/notifications.py: pluggable delivery via console, webhook, Telegram, or email (SMTP) - stdlib only, never crashes on missing config - New
demo/run_scheduler.py: production entry point wiring the scheduler to real briefing generation and delivery - 77 new unit tests (
test_storage.py,test_tools.py,test_scheduler.py,test_notifications.py) - total suite grew from 36 to 113 tests, all passing
v1.4.0 - Real Correlation Engine
- New
demo/analytics.pymodule: pure-stdlib Pearson correlation analysis across mood, sleep, stress, energy, and hydration detect_patterns()now computes actual daily-aggregated correlations (r-value, day count, direction, strength) instead of a static placeholder message ("correlation analysis active")- Correlation insights are surfaced automatically in
detect_patternstool output, feeding into morning/evening/weekly briefings - 14 new unit tests covering the correlation engine (
tests/test_analytics.py)
v1.3.0 - Dream Journal
- Dream logging mode with symbol, emotion, tone and vividness tracking
- Sleep/mood/stress/dream correlation detection
- Recurring symbol pattern detection across 30 days
- Morning briefing includes dream analysis
v1.2.0 - Voice & Performance
- Voice mode - speak to Hermes, hear responses via system TTS
- Concurrent tool execution - read-only tools run in parallel threads
- Microphone input via SpeechRecognition
v1.1.0 - Health & Wellness Expansion
- Nutrition, sleep, hydration, fitness, mental, focus tracking
- Full health dashboard and weekly health report
- Interactive chat mode
v1.0.0 - Initial Release
- 12 demo modes covering every life dimension
- Pattern detection across mood, sleep, nutrition, stress, focus
- Memory-driven briefings, Atropos RL environment
python -m pytest tests/ -v
python -c "from environments.life_os_env import smoke_test; smoke_test()"Every other agent in this hackathon does something for you. Hermes Life OS becomes something with you.
It tracks nutrition, sleep, fitness, stress, focus, hydration, habits, and goals - and connects them all. Bad Monday? It checks if you slept poorly Sunday. Energy crash at 3pm? It looks at what you ate for lunch. Mood dip this week? It finds the pattern you missed.
That is not a tool. That is a presence that accumulates.
