daily-briefing
Generates a compact, warm daily message suitable for cron delivery (stdout/chat reply). Always succeeds even with minimal context.
Skill Type: System Skill (Orchestrator Pattern)
This skill uses the System Skill pattern for execution on macOS. The agent must:
- 1. Never run raw CLI commands directly (except
curl for weather). - Always invoke the runner script to gather data.
- Read gathered data from JSON after the script completes.
- Generate the briefing text using the agent's own capabilities.
Quick reference:
# Invoke data gatherer (waits for completion)
"{baseDir}/skills/daily-briefing/bin/run_daily_briefing.sh"
# Read output
cat /tmp/daily_briefing_data.json
Output Contract (STRICT)
CRITICAL: Output only the briefing text. No prefaces, no explanations, no "Done", no file paths, no tool output, no markdown code fences around the briefing.
Line 1 Format (Required)
Line 1 must begin exactly with the time-appropriate greeting:
CODEBLOCK1
- - Use full month name (e.g., "February", not "Feb").
- If today is the user's birthday (matched by name in contacts): replace greeting with:
CODEBLOCK2
Greeting Selection (Local Time)
| Time Range | Greeting |
|---|
| 05:00–11:59 | Good morning |
| 12:00–16:59 |
Good afternoon |
| 17:00–21:59 | Good evening |
| 22:00–04:59 | Good night |
| Unknown | Good morning (default) |
Skies Sentence Rules
If weather is usable:
CODEBLOCK3
- - Use high temp if reliable → time clause: " this afternoon"
- Otherwise use current temp → time clause: " right now"
- If low exists: append INLINECODE1
- If precip chance ≥30%: append INLINECODE2
If weather is not usable: Use exact fallback:
CODEBLOCK4
Layout Rules
CODEBLOCK5
- - Always include a blank line after Line 1.
- Each section separated by a blank line if present.
- Target ~5–15 lines depending on enabled integrations.
Vibe and Tone
- - Gentle gift for the day: warm, calm, compassionate, quietly hopeful.
- No scolding, no urgency, no productivity pressure.
- Telegram-friendly: short lines, roomy spacing, easy to skim.
System Skill Execution
Step 1: Check Mode (Interactive vs Cron)
If interactive AND missing critical info (location/timezone/units):
- - Prompt briefly for missing info before generating briefing.
- Offer toggles for integrations.
- Mention the important emails feature: explain it uses AI-powered semantic analysis to surface actionable emails (transactions, shipments, security alerts, etc.) and can be enabled via
emails.enabled in config; note iCloud Mail requires an app-specific password (emails.icloudPassword).
If non-interactive (cron/automation):
- - Do NOT ask questions (cron-safe). Use defaults.
- Do NOT create/modify any files.
- Do NOT spawn background tasks/sub-agents.
- Omit weather if location is unavailable.
Step 2: Invoke the Data Gatherer
CODEBLOCK6
- - The runner script executes
scripts/daily_briefing_orchestrator.sh. - TCC permissions are granted to Terminal.app (or the calling process).
Step 3: Read the Gathered Data
After the app completes, read:
CODEBLOCK7
JSON structure:
CODEBLOCK8
Step 4: Fetch Weather (Agent Task)
The agent must fetch weather directly using curl (not via orchestrator):
CODEBLOCK9
- - Location: Use
config.location from gathered data; if empty/null, weather is unavailable. - Retry: Retry once on failure.
- If still failing or unusable: Weather is unavailable; use fallback sentence.
Parse from JSON response:
- - Conditions: INLINECODE8
- Current temp (C): INLINECODE9
- Current temp (F): INLINECODE10
- High temp (C): INLINECODE11
- High temp (F): INLINECODE12
- Low temp (C): INLINECODE13
- Low temp (F): INLINECODE14
- Precip chance: max of
weather[0].hourly[*].chanceofrain (as integers)
Units: Use config.units ("C" or "F"). Default to Celsius if unknown.
CRITICAL: Do NOT output raw curl/tool output. Do NOT use wttr.in one-line formats.
Step 5: Classify Important Emails (Agent Task)
Only if config.emails_enabled is true and emails.available is true.
For each email in emails.data, use the agent's own semantic analysis to determine importance.
Important Email Criteria (any match qualifies):
- - From contacts in the gathered contacts list
- Order shipment notifications
- Receipts for purchases or transaction confirmations
- Incoming/outgoing transaction alerts
- Refund-related messages
- Customer service interactions
- Upcoming subscription renewal notices
- Upcoming payment heads-up notices
- Technical newsletters
- Job application updates
- Messages from recruiters (exclude WITCH-like outsourcing firms)
- Banking alerts
- Calendar invites
- Account security emails (e.g., "your account is locked")
- Shared items (e.g., Google Drive shares)
- Wishlist-related alerts
- Starred/flagged emails (positive signal, not sole determinant)
- Any other contextually important emails
Exclusions: The following are never important, even if matching other criteria:
- - Promotional/marketing emails
- LinkedIn Job Alert emails (LinkedIn message notifications are fine)
- Unsolicited recruiter/job-posting emails and mass hiring notices (e.g., subjects or bodies containing keywords like "hire", "hiring", "job", "position", "onsite", "fulltime", "recruiter", "application", or obvious bulk outreach), unless the sender is in the user's contacts or the message is starred/readily identified as personally relevant.
- Product announcement / product update emails and vendor/platform notifications (e.g., "[Product Update]", release announcements, automatic enablement notices), unless the sender is in the user's contacts or the message is explicitly starred.
- Vendor newsletters, community announcements, and general technical mailing-list posts (e.g., blog posts, release notes, product previews, digests), unless clearly personal or from a contact.
Failure behavior: If semantic analysis fails, silently omit the entire email section.
Apply filters and sorting:
- 1. Filter by
emails_unread_only if true - If
emails_starred_first is true, starred emails first - Sort by date per INLINECODE22
- Limit to INLINECODE23
Step 6: Generate the Briefing
Using all gathered and processed data, compose the briefing text following the Output Contract.
Section Formats:
Birthdays:
🎂 **Birthdays:**
• Today: Name
• Feb 5: Name
- - Group multiples per date
- Today entries first
- Up to 5 upcoming (excluding today)
Calendar Events:
📅 **Today's schedule:**
• All-day: Event title
• 9:00 AM: Event title
- - Single day: "Today's schedule"
- Multi-day: "Schedule" with "Today/Tomorrow/{Month} {D}" labels
- All-day events first, then timed by start
- Up to 3 events per day
Reminders:
✅ **Reminders:**
• Pick up prescription
- - Due-today reminders only
- Up to 3 reminders
Important Emails:
📧 **Emails needing attention:**
• Amazon: Your order has shipped
• Chase: Payment received
Anchors:
- - Only if you can confidently infer 1–3 real priorities from user-provided context.
- Plain bullets, no heading.
- If not real/uncertain, omit entirely (do not invent).
Closing Line:
- - Required. Use the
quote field from the gathered JSON data. - The orchestrator provides a random motivational quote each run.
Step 7: Output the Briefing
Return only the briefing text. Nothing else.
Configuration
Configuration is read from ~/.openclaw/openclaw.json at path skills.entries.daily-briefing.config:
CODEBLOCK14
Configuration Options
| Option | Type | Default | Description |
|---|
| INLINECODE28 | string | "" | Location for weather (e.g., "New York, NY") |
| INLINECODE29 |
string | system | Timezone (e.g., "America/New_York") |
|
units | string | "C" | Temperature units: "C" or "F" |
|
birthdays.enabled | bool | true | Enable birthday tracking |
|
birthdays.lookahead | int | 14 | Days ahead to show upcoming birthdays |
|
birthdays.sources | array | ["contacts"] | Sources: "contacts" (iCloud), "google" |
|
calendar.enabled | bool | true | Enable calendar events |
|
calendar.lookahead | int | 0 | Days ahead (0 = today only) |
|
calendar.sources | array | ["google", "icloud"] | Calendar sources |
|
reminders.enabled | bool | true | Enable Apple Reminders |
|
reminders.dueFilter | string | "today" | Due date filter: "today", "week", or "all" |
|
reminders.includePastDue | bool | true | Include overdue/past-due reminders |
|
emails.enabled | bool | false | Enable important emails feature |
|
emails.icloudPassword | string | "" | iCloud Mail app-specific password |
|
emails.limit | int | 10 | Maximum emails to show |
|
emails.sortNewest | bool | true | Sort newest first |
|
emails.starredFirst | bool | true | Prioritize starred emails |
|
emails.unreadOnly | bool | true | Only show unread emails |
Defaults
- - Timezone: User's local timezone; fallback to UTC if unknown.
- Location: User's location if present; omit weather if unavailable in cron mode.
- Units: User's preferred units if known; otherwise Celsius.
Dependencies
Required:
- -
curl — for weather fetching - INLINECODE47 — for orchestrator script
Optional:
- -
gog — brew install steipete/tap/gogcli (Google Calendar, Gmail, Contacts) - INLINECODE50 —
brew install ajrosen/tap/icalpal (iCloud Calendar) - INLINECODE52 —
brew install himalaya (iCloud Mail via IMAP)
File Structure
CODEBLOCK15
Example Output
CODEBLOCK16
Cleanup
CODEBLOCK17
daily-briefing
生成一条简洁、温暖的每日消息,适合通过 cron 投递(标准输出/聊天回复)。即使在上下文极少的情况下也能成功执行。
技能类型:系统技能(编排器模式)
本技能使用 系统技能模式 在 macOS 上执行。代理必须:
- 1. 绝不直接运行原始 CLI 命令(curl 获取天气除外)。
- 始终调用运行脚本 来收集数据。
- 从 JSON 读取收集到的数据 在脚本完成后。
- 使用代理自身能力生成简报文本。
快速参考:
bash
调用数据收集器(等待完成)
{baseDir}/skills/daily-briefing/bin/run
dailybriefing.sh
读取输出
cat /tmp/daily
briefingdata.json
输出契约(严格)
关键: 仅输出 简报文本。不要前言、不要解释、不要完成、不要文件路径、不要工具输出、不要在简报周围添加 markdown 代码围栏。
第 1 行格式(必需)
第 1 行 必须以 符合时段的问候语开头:
Good {timeofday} - Today is {Weekday}, {Month} {D}, {YYYY}. {Skies sentence}.
- - 使用 完整月份名称(例如 February,而非 Feb)。
- 如果今天是用户的生日(通过联系人中的姓名匹配):将问候语替换为:
🎉 Happy Birthday! Today is {Weekday}, {Month} {D}, {YYYY}. {Skies sentence}.
问候语选择(本地时间)
| 时间段 | 问候语 |
|---|
| 05:00–11:59 | Good morning |
| 12:00–16:59 |
Good afternoon |
| 17:00–21:59 | Good evening |
| 22:00–04:59 | Good night |
| 未知 | Good morning(默认) |
天气描述句规则
如果天气数据可用:
{Conditions} skies, around {TEMP}°{timeclause}{lowclause}{precip_clause}.
- - 如果高温可靠则使用 → 时间从句: this afternoon
- 否则使用当前温度 → 时间从句: right now
- 如果存在低温:追加 , with a low around {LOW}°
- 如果降水概率 ≥30%:追加 , and a {CHANCE}% chance of {rain/snow/precipitation}
如果天气数据不可用: 使用精确的备用句:
I cant access weather right now.
布局规则
{第 1 行:问候语及天气描述句}
{生日部分 - 仅当今天或近期有生日时}
{日历事件部分 - 仅当有事件时}
{提醒事项部分 - 仅当有提醒时}
{重要邮件部分 - 仅当启用且有邮件时}
{锚点 - 仅当从上下文中有真实优先级时}
{结束语 - 始终必需}
- - 第 1 行后始终包含一个 空行。
- 每个部分(如果存在)之间用空行分隔。
- 目标 约 5–15 行,取决于启用的集成。
氛围与语气
- - 温柔的每日馈赠:温暖、平静、富有同情心、安静地充满希望。
- 没有责备、没有紧迫感、没有生产力压力。
- Telegram 友好:短行、宽松间距、易于浏览。
系统技能执行
第 1 步:检查模式(交互式 vs Cron)
如果是交互式且缺少关键信息(位置/时区/单位):
- - 在生成简报前简要提示缺失信息。
- 提供集成开关选项。
- 提及重要邮件功能:说明它使用 AI 驱动的语义分析来呈现可操作的邮件(交易、发货、安全警报等),可通过配置中的 emails.enabled 启用;注意 iCloud 邮件需要应用专用密码(emails.icloudPassword)。
如果是非交互式(cron/自动化):
- - 不要提问(cron 安全)。使用默认值。
- 不要创建/修改任何文件。
- 不要生成后台任务/子代理。
- 如果位置不可用,省略天气。
第 2 步:调用数据收集器
bash
{baseDir}/skills/daily-briefing/bin/rundailybriefing.sh
- - 运行脚本执行 scripts/dailybriefingorchestrator.sh。
- TCC 权限已授予 Terminal.app(或调用进程)。
第 3 步:读取收集到的数据
应用完成后,读取:
/tmp/dailybriefingdata.json
JSON 结构:
json
{
generated_at: ISO 时间戳,
system: {
timezone: America/New_York,
local_time: 2024-02-03T08:30:00,
hour: 8
},
config: {
location: New York, NY,
units: C,
birthdays_enabled: true,
birthdays_lookahead: 14,
calendargoogleenabled: true,
calendaricloudenabled: true,
calendar_lookahead: 0,
reminders_enabled: true,
remindersduefilter: today,
remindersincludepast_due: true,
emails_enabled: false,
emails_limit: 10,
emailssortnewest: true,
emailsstarredfirst: true,
emailsunreadonly: true
},
birthdays: {
available: true,
userbirthdaytoday: false,
data: [
{name: Jane Doe, date: 2024-02-03, days_until: 0},
{name: John Smith, date: 2024-02-05, days_until: 2}
]
},
calendar: {
available: true,
data: [
{title: 团队站会, start: 09:00, end: 09:30, all_day: false, date: 2024-02-03, source: google},
{title: 医生预约, start: null, end: null, all_day: true, date: 2024-02-03, source: icloud}
]
},
reminders: {
available: true,
data: [
{title: 取处方药, due: 2024-02-03}
]
},
emails: {
available: true,
data: [
{id: abc123, from: Amazon, from_email: shipment@amazon.com, subject: 您的订单已发货, preview: 您的包裹正在途中..., starred: false, unread: true, date: 2024-02-03T07:30:00Z, source: gmail},
{id: def456, from: Chase, from_email: alerts@chase.com, subject: 已收到付款, preview: 我们已收到您的付款..., starred: true, unread: true, date: 2024-02-03T06:15:00Z, source: icloud}
]
},
contacts: {
available: true,
data: [
{name: Jane Doe, email: jane@example.com},
{name: John Smith, email: john@example.com}
]
}
}
第 4 步:获取天气(代理任务)
代理必须直接使用 curl 获取天气(不通过编排器):
bash
curl -fsSL --max-time 12 https://wttr.in/{ENCODED_LOCATION}?format=j1
- - 位置: 使用收集数据中的 config.location;如果为空/空值,则天气不可用。
- 重试: 失败时重试一次。
- 如果仍然失败或不可用: 天气不可用;使用备用句。
从 JSON 响应中解析:
- - 天气状况:currentcondition[0].weatherDesc[0].value
- 当前温度(C):currentcondition[0].tempC
- 当前温度(F):currentcondition[0].temp_F
- 高温(C):weather[0].maxtempC
- 高温(F):weather[0].maxtempF
- 低温(C):weather[0].mintempC
- 低温(