Owner Briefing
by @netanel-abergel
Generate and send a daily briefing to your owner covering today's meetings, urgent emails, open tasks, and anything that needs attention. Use when: it's the...
clawhub install owner-briefingπ About This Skill
name: owner-briefing description: "Generate and send a daily briefing to your owner covering today's meetings, urgent emails, open tasks, and anything that needs attention. Use when: it's the start of the owner's day, when asked for a summary, or on a scheduled cron job."
Owner Briefing Skill
Minimum Model
Any small model. Data collection is CLI-based. Formatting is simple.When to Send (Decision Rules)
Briefing Format
βοΈ Good morning [Owner Name] β here's your day:π
TODAY'S MEETINGS
β’ 09:30 β Standup (30 min)
β’ 14:00 β 1:1 with [Person] (1h)
π¬ EMAILS NEEDING ATTENTION
β’ [Sender] β "[Subject]"
β
OPEN TASKS
β’ [Task from monday.com or memory]
β οΈ HEADS UP
β’ [Deadline, unusual item, etc.]
Have a great day! π
Step 1 β Get Today's Calendar Events
#!/bin/bash
set -eCalculate today and tomorrow in UTC ISO format
TODAY=$(date -u +%Y-%m-%dT00:00:00Z)
TOMORROW=$(
date -u -d '+1 day' +%Y-%m-%dT00:00:00Z 2>/dev/null \
|| date -u -v+1d +%Y-%m-%dT00:00:00Z
)Fetch events from Google Calendar via gog
GOG_ACCOUNT=owner@company.com gog calendar events primary \
--from "$TODAY" \
--to "$TOMORROW" \
2>/dev/null \
| python3 -c "
import sys, jsonParse JSON, default to empty list on error
try:
events = json.loads(sys.stdin.read().strip() or '[]')
except json.JSONDecodeError:
events = []print('π
TODAY\'S MEETINGS')
if not events:
print('β’ No events')
else:
# Sort by start time, format each event
for e in sorted(events, key=lambda x: x.get('start', {}).get('dateTime', '')):
start = e.get('start', {}).get('dateTime', '')[:16].replace('T', ' ')
title = e.get('summary', 'Untitled')
print('β’', start, 'β', title)
"
Step 2 β Get Urgent Emails
#!/bin/bash
set -eFetch up to 5 unread emails from the last day
GOG_ACCOUNT=owner@company.com gog gmail search \
'is:unread newer_than:1d' \
--max 5 \
2>/dev/null \
| python3 -c "
import sys, jsonParse JSON, default to empty list on error
try:
emails = json.loads(sys.stdin.read().strip() or '[]')
except json.JSONDecodeError:
emails = []print('π¬ EMAILS NEEDING ATTENTION')
if not emails:
print('β’ No urgent emails')
else:
for e in emails:
sender = e.get('from', 'Unknown')
subject = e.get('subject', '(no subject)')
print('β’', sender, 'β', '\"' + subject + '\"')
"
Step 3 β Get Open Tasks (monday.com)
#!/bin/bash
set -eTOKEN_FILE="$HOME/.credentials/monday-api-token.txt"
If token is missing, skip this section gracefully
if [ ! -f "$TOKEN_FILE" ]; then
echo "β
OPEN TASKS"
echo "β’ (monday.com token not configured β skipping)"
exit 0
fiMONDAY_TOKEN=$(cat "$TOKEN_FILE")
BOARD_ID="BOARD_ID" # replace with actual board ID
Fetch first 5 items from the board
RESPONSE=$(curl -s -X POST https://api.monday.com/v2 \
-H "Content-Type: application/json" \
-H "Authorization: $MONDAY_TOKEN" \
-d "{\"query\": \"{ boards(ids: [$BOARD_ID]) { items_page(limit: 5) { items { name state } } } }\"}")Print open (non-done) items
echo "$RESPONSE" | python3 -c "
import sys, jsontry:
d = json.loads(sys.stdin.read())
items = d['data']['boards'][0]['items_page']['items']
except (KeyError, IndexError, json.JSONDecodeError) as e:
print('β
OPEN TASKS')
print('β’ (could not fetch:', e, ')')
sys.exit(0)
Filter out completed items
open_items = [i for i in items if i.get('state') != 'done']print('β
OPEN TASKS')
if not open_items:
print('β’ None β all clear!')
else:
for item in open_items:
print('β’', item['name'])
"
Step 4 β Assemble and Send the Briefing
Run Steps 1β3, save each output to a temp file, then combine:
#!/bin/bash
set -eRun each section and capture output
CALENDAR_SECTION=$(bash step1-calendar.sh 2>/dev/null || echo "π
TODAY'S MEETINGS\nβ’ (unavailable)")
EMAIL_SECTION=$(bash step2-email.sh 2>/dev/null || echo "π¬ EMAILS NEEDING ATTENTION\nβ’ (unavailable)")
TASKS_SECTION=$(bash step3-tasks.sh 2>/dev/null || echo "β
OPEN TASKS\nβ’ (unavailable)")Build the briefing message
BRIEFING="βοΈ Good morning β here's your day:$CALENDAR_SECTION
$EMAIL_SECTION
$TASKS_SECTION"
Option A: Send via WhatsApp
openclaw message send --to OWNER_PHONE --message "$BRIEFING"Option B: Send via email
GOG_ACCOUNT=owner@company.com gog gmail send \
--to owner@company.com \
--subject "βοΈ Your Daily Briefing β $(date +'%A %B %d')" \
--body "$BRIEFING"
Cron Schedule
{
"jobs": [
{
"id": "morning-briefing",
"schedule": "30 7 * * 1-5",
"timezone": "Asia/Jerusalem",
"task": "Generate and send the owner's morning briefing: calendar events, urgent emails, and open tasks. Use owner-briefing skill.",
"delivery": {
"mode": "message",
"channel": "whatsapp",
"to": "OWNER_PHONE"
}
}
]
}
"30 7" to adjust time."timezone" to match the owner's location.Customization Options
| Goal | Change |
|---|---|
| Only meetings after 9am | Filter events: if start_hour >= 9 |
| Skip internal standups | Filter: if 'standup' not in summary.lower() |
| Add weather | Call weather skill before building briefing |
| Highlight flagged emails | Change search to is:starred or is:important |
| Evening summary (tomorrow preview) | Change TODAY/TOMORROW to tomorrow's dates |
What NOT to Include
The briefing is for action, not recap. Apply this filter before sending:
A good briefing takes 30 seconds to read. If it's longer, cut more.
Cost Tips
newer_than:1d.