Water Coach
by @oristides
Hydration tracking and coaching skill. Use when user wants to track water intake, get reminders to drink water, log body metrics (weight, body fat, muscle %,...
clawhub install water-coachπ About This Skill
name: water-coach description: "Hydration tracking and coaching skill. Use when user wants to track water intake, get reminders to drink water, log body metrics, or get analytics on hydration habits." compatibility: "Requires python3, openclaw cron feature, heartbeat feature" metadata: {"clawdbot":{"emoji":"π§"} author: oristides
π§ Water Coach
First-time Setup
Follow the first setup here references/setup.md
π€ How Other Agents Should Interact
First-time Setup Check
water setup
| is_setup | What to do |
|----------|-------------|
| false | Ask: weight OR desired goal. Also ask: "What times do you want water reminders?" (let user configure their schedule). Then use water set_body_weight 80 or water set_goal 3000. Don't assume hardcoded times! |
| true | Skip setup. Just log water or show status. |
β Don't Ask
β Do Ask
CLI Structure
water_coach.py [options]
Namespaces: water | body | analytics
Data Format
CSV Format
logged_at,drank_at,date,slot,ml_drank,goal_at_time,message_id
| Column | Description | |--------|-------------| | logged_at | When user told you (NOW) | | drank_at | When user actually drank (user can specify past time) | | date | Derived from drank_at | | slot | morning/lunch/afternoon/evening/manual | | ml_drank | Amount in ml | | goal_at_time | Goal at that moment | | message_id | Audit trail - link to conversation |
Key Rules:
Details at references/log_format.md
Audit Trail
Every water log entry captures:
water audit to get entry + conversation context# Check proof of a water entry
water audit msg_123
Returns: entry data + surrounding messages for context
> β οΈ Privacy Notice: The audit feature can read your conversation transcripts, but only when you explicitly run water audit . This is off by default (audit_auto_capture: false).
>
>
> # Edit water_config.json and set:
> "audit_auto_capture": true
>
>
> How it works:
> - Water log always saves the message_id (regardless of this setting) β
> - When you run water audit :
> - If false: Shows entry data only (message_id saved, but no context read)
> - If true: Also reads transcript to show conversation context ("User said: I drank 500ml")
>
> Why disable it? If you discuss sensitive topics and don't need proof of intake, leave it off.Daily Commands
# Water
water status # Current progress (calculated from drank_at)
water log 500 # Log intake (drank_at = now)
water log 500 --drank-at=2026-02-18T18:00:00Z # Log with past time
water log 500 --drank-at=2026-02-18T18:00:00Z --message-id=msg_123
water dynamic # Check if extra notification needed
water threshold # Get expected % for current hour
water set_body_weight 80 # Update weight + logs to body_metrics
water set_body_weight 80 --update-goal # + update goal
water audit # Get entry + conversation contextBody
body log --weight=80 --height=1.75 --body-fat=18
body latest # Get latest metrics
body history 30 # Get historyAnalytics
analytics week # Weekly briefing (Sunday 8pm)
analytics month # Monthly briefing (2nd day 8pm)
Rules (MUST FOLLOW)
1. ALWAYS use CLI - never calculate manually
2. LLM interprets first - "eu tomei 2 copos" β 500ml β water log 500
3. Threshold from CLI - run water threshold, don't hardcode
4. GOAL is USER'S CHOICE - weight Γ 35 is just a DEFAULT suggestion:
- At setup: Ask weight β suggest goal β CONFIRM with user
- On weight update: Ask "Want to update your goal to the new suggested amount?"
- User can set any goal (doctor's orders, preference, etc.)
Config Tree
water-coach/
βββ SKILL.md β You are here
βββ scripts/
β βββ water_coach.py β Unified CLI
β βββ water.py β Core functions
βββ data/ β DO NOT USE - keep skill code separate from user data
βββ references/
βββ setup.md
βββ dynamic.md
βββ log_format.md
β οΈ IMPORTANT: Data Location
User data is stored in the AGENT WORKSPACE, NOT in the skill folder!| Data | Location |
|------|----------|
| water_log.csv | |
| water_config.json | |
| body_metrics.csv |
Example path: /home/oriel/.openclaw/workspace/memory/data/
Why? Keeps user data separate from skill code β makes backups, migrations, and skill updates easier.
Notifications Schedule
| Type | When | Command | |------|------|---------| | User Configured | Per user's schedule | water status | | Default Suggestion | 9am, 12pm, 3pm, 6pm, 9pm | water status | | Dynamic | Every ~30 min (heartbeat) | water dynamic | | Weekly | Sunday 8pm | analytics week | | Monthly | 2nd day 8pm | analytics month |
Notification Rules (MUST FOLLOW)
Quick Reference
| Task | Command |
|------|---------|
| Check progress | water_coach.py water status |
| Log water | water_coach.py water log 500 |
| Need extra? | water_coach.py water dynamic |
| Body metrics | water_coach.py body log --weight=80 |
| Weekly report | water_coach.py analytics week |
| Monthly report | water_coach.py analytics month |
Dynamic Scheduling details
β references/dynamic.mdβ οΈ Bug Fix (Feb 2026)
The water dynamic command had a bug where the hourly notification counter wouldn't reset when the hour changed. This is now fixed:
The script now checks if the current hour differs from last_extra_hour and resets the counter accordingly
This ensures notifications work correctly after hour boundaries (e.g., 4PM) β οΈ Bug Fix (Feb 2026) - Analytics
The analyticsPM β 5 week and analytics month commands had a bug:
Was looking for non-existent cumulative_ml column in CSV
Fixed to sum ml_drank per day insteadβ
How to Build Good Weekly/Monthly Reports
Use these functions (don't reinvent):
| Report | Script | Function |
|--------|--------|----------|
| Weekly |
water_coach.py analytics week | analytics_week() in water_coach.py |
| Monthly | water_coach.py analytics month | analytics_month() in water_coach.py |These call
get_week_stats() and get_month_stats() in water.py`.When updating analytics functions, follow these rules:
1. Include ALL days, even with 0ml
# In get_week_stats() / get_month_stats()
Include every day in the range, not just days with data
for i in range(days):
d = (date.today() - timedelta(days=i)).strftime("%Y-%m-%d")
ml = by_date.get(d, {}).get("ml", 0) # Default to 0, not skip
2. Calculate true average
# Average = total_ml / ALL days (including zeros), not just tracked days
avg_ml = total_ml / days # e.g., 15440ml / 7 days = 2205ml/day
3. Show all days in table format
| Dia | ML | % | Status |
| Sab 22 | 2250ml | 67.7% | β οΈ |
| Seg 17 | 0ml | 0.0% | β |
This gives users an accurate picture of their habits!
Tests
python3 -m pytest skills/water-coach/scripts/test/test_water.py -v
Example
User: "eu tomei 2 copos"
Agent: (LLM interprets: 2 copos β 500ml)
Agent: exec("water_coach.py water log 500")
β Python logs to CSV
Agent Evaluations β evaluation/AGENT.md
π‘ Examples
User: "eu tomei 2 copos"
Agent: (LLM interprets: 2 copos β 500ml)
Agent: exec("water_coach.py water log 500")
β Python logs to CSV
Agent Evaluations β evaluation/AGENT.md