Skill: antgly/daily-briefing (version 1.0.5)

--- name: daily-briefing description: Generates a warm, compact daily briefing with weather, calendar, reminders, birthdays, and important emails for cron or chat delivery. metadata: {"openclaw":{"emoji":"🌅","requires":{"os":["darwin"],"bins":["curl","bash"]},"optional_bins":["icalpal","gog","himalaya"]}} user-invocable: true --- # 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). 2. **Always invoke the runner script** to gather data. 3. **Read gathered data from JSON** after the script completes. 4. **Generate the briefing text** using the agent's own capabilities. **Quick reference:** ```bash # 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: ``` Good {time_of_day} - Today is {Weekday}, {Month} {D}, {YYYY}. {Skies sentence}. ``` - Use **full month name** (e.g., "February", not "Feb"). - If today is the user's birthday (matched by name in contacts): replace greeting with: ``` 🎉 Happy Birthday! Today is {Weekday}, {Month} {D}, {YYYY}. {Skies sentence}. ``` ### 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:** ``` {Conditions} skies, around {TEMP}°{time_clause}{low_clause}{precip_clause}. ``` - Use **high temp** if reliable → time clause: " this afternoon" - Otherwise use **current temp** → time clause: " right now" - If low exists: append `, with a low around {LOW}°` - If precip chance ≥30%: append `, and a {CHANCE}% chance of {rain/snow/precipitation}` **If weather is not usable:** Use exact fallback: ``` I can't access weather right now. ``` ### Layout Rules ``` {Line 1: Greeting with skies sentence} {Birthdays section - only if any today or upcoming} {Calendar events section - only if any} {Reminders section - only if any} {Important emails section - only if enabled and any} {Anchors - only if real priorities from context} {Closing line - always required} ``` - 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 ```bash "{baseDir}/skills/daily-briefing/bin/run_daily_briefing.sh" ``` - 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: ``` /tmp/daily_briefing_data.json ``` JSON structure: ```json { "generated_at": "ISO timestamp", "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, "calendar_google_enabled": true, "calendar_icloud_enabled": true, "calendar_lookahead": 0, "reminders_enabled": true, "reminders_due_filter": "today", "reminders_include_past_due": true, "emails_enabled": false, "emails_limit": 10, "emails_sort_newest": true, "emails_starred_first": true, "emails_unread_only": true }, "birthdays": { "available": true, "user_birthday_today": 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": "Team standup", "start": "09:00", "end": "09:30", "all_day": false, "date": "2024-02-03", "source": "google"}, {"title": "Doctor appointment", "start": null, "end": null, "all_day": true, "date": "2024-02-03", "source": "icloud"} ] }, "reminders": { "available": true, "data": [ {"title": "Pick up prescription", "due": "2024-02-03"} ] }, "emails": { "available": true, "data": [ {"id": "abc123", "from": "Amazon", "from_email": "[REDACTED_SECRET]", "subject": "Your order has shipped", "preview": "Your package is on its way...", "starred": false, "unread": true, "date": "2024-02-03T07:30:00Z", "source": "gmail"}, {"id": "def456", "from": "Chase", "from_email": "[REDACTED_SECRET]", "subject": "Payment received", "preview": "We received your payment of...", "starred": true, "unread": true, "date": "2024-02-03T06:15:00Z", "source": "icloud"} ] }, "contacts": { "available": true, "data": [ {"name": "Jane Doe", "email": "[REDACTED_SECRET]"}, {"name": "John Smith", "email": "[REDACTED_SECRET]"} ] } } ``` ### Step 4: Fetch Weather (Agent Task) The agent must fetch weather directly using `curl` (not via orchestrator): ```bash curl -fsSL --max-time 12 "https://wttr.in/{ENCODED_LOCATION}?format=j1" ``` - **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: `current_condition[0].weatherDesc[0].value` - Current temp (C): `current_condition[0].temp_C` - Current temp (F): `current_condition[0].temp_F` - High temp (C): `weather[0].maxtempC` - High temp (F): `weather[0].maxtempF` - Low temp (C): `weather[0].mintempC` - Low temp (F): `weather[0].mintempF` - 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 2. If `emails_starred_first` is true, starred emails first 3. Sort by date per `emails_sort_newest` 4. Limit to `emails_limit` ### 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 ``` - Format: `• Sender: Subject (truncated if needed)` **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`: ```json { "skills": { "entries": { "daily-briefing": { "config": { "location": "New York, NY", "timezone": "America/New_York", "units": "C", "birthdays": { "enabled": true, "lookahead": 14, "sources": ["contacts", "google"] }, "calendar": { "enabled": true, "lookahead": 0, "sources": ["google", "icloud"] }, "reminders": { "enabled": true }, "emails": { "enabled": false, "icloudPassword": "", "limit": 10, "sortNewest": true, "starredFirst": true, "unreadOnly": true } } } } } } ``` ### Configuration Options | Option | Type | Default | Description | |--------|------|---------|-------------| | `location` | string | "" | Location for weather (e.g., "New York, NY") | | `timezone` | 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 - `bash` — for orchestrator script **Optional:** - `gog` — `brew install steipete/tap/gogcli` (Google Calendar, Gmail, Contacts) - `icalpal` — `brew install ajrosen/tap/icalpal` (iCloud Calendar) - `himalaya` — `brew install himalaya` (iCloud Mail via IMAP) --- ## File Structure ``` daily-briefing/ ├── SKILL.md ├── README.md ├── install.sh ├── scripts/ │ └── daily_briefing_orchestrator.sh └── bin/ └── run_daily_briefing.sh (created by install.sh) ``` --- ## Example Output ``` Good morning - Today is Saturday, February 3, 2024. Partly cloudy skies, around 45°F this afternoon, with a low around 32°F. 🎂 **Birthdays:** • Today: Jane Doe • Feb 5: John Smith 📅 **Today's schedule:** • All-day: Doctor appointment • 9:00 AM: Team standup ✅ **Reminders:** • Pick up prescription 📧 **Emails needing attention:** • Amazon: Your order has shipped • Chase: Payment received Take things one step at a time today—you've got this. ``` --- ## Cleanup ```bash "{baseDir}/skills/daily-briefing/bin/run_daily_briefing.sh" --cleanup ```

path: README.md
content: # daily-briefing

🌅 Generates a warm, compact daily briefing with weather, calendar, reminders, birthdays, and important emails for cron or chat delivery.

## Features

- **Weather** — Current conditions, highs/lows, precipitation chances via wttr.in
- **Calendar Events** — Unified view from Google Calendar and iCloud Calendar
- **Reminders** — Due and past-due items from Apple Reminders
- **Birthdays** — Today and upcoming from iCloud Contacts and Google Contacts
- **Important Emails** — AI-powered semantic analysis to surface actionable emails (transactions, shipments, security alerts, etc.)
- **Cron-safe** — Always succeeds with sensible defaults; never prompts in automation mode
- **Warm, calm tone** — A gentle gift for the day with no productivity pressure

## Installation

### 1. Install via ClawHub (recommended)

```bash
clawhub install daily-briefing
```

Or copy manually to any skills directory:
- `~/.openclaw/skills/daily-briefing`
- `<workspace>/skills/daily-briefing`

### 2. Run the installer

```bash
cd <skill-directory>
chmod +x install.sh
./install.sh
```

### 3. Grant macOS permissions

Grant Terminal.app (or your terminal) access in **System Settings > Privacy & Security**:

| Permission | Required for |
|------------|--------------|
| Contacts | Birthday tracking from iCloud Contacts |
| Calendars | iCloud Calendar events (via icalpal) |
| Reminders | Apple Reminders (via remindctl) |

### 4. Install optional CLI tools

```bash
# Google services (Calendar, Gmail, Contacts)
brew install steipete/tap/gogcli

# iCloud Calendar
brew install ajrosen/tap/icalpal

# Apple Reminders
brew install steipete/tap/remindctl

# iCloud Mail
brew install himalaya
```

## Configuration

Edit `~/.openclaw/openclaw.json`:

```json
{
  "skills": {
    "entries": {
      "daily-briefing": {
        "config": {
          "location": "New York, NY",
          "timezone": "America/New_York",
          "units": "F",
          "birthdays": {
            "enabled": true,
            "lookahead": 14,
            "sources": ["contacts", "google"]
          },
          "calendar": {
            "enabled": true,
            "lookahead": 0,
            "sources": ["google", "icloud"]
          },
          "reminders": {
            "enabled": true,
            "dueFilter": "today",
            "includePastDue": true
          },
          "emails": {
            "enabled": false,
            "icloudPassword": "",
            "limit": 10,
            "sortNewest": true,
            "starredFirst": true,
            "unreadOnly": true
          }
        }
      }
    }
  }
}
```

### Configuration Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `location` | string | "" | Location for weather (e.g., "New York, NY") |
| `timezone` | 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 for himalaya |
| `emails.limit` | int | 10 | Maximum emails to show |
| `emails.sortNewest` | bool | true | Sort newest first |
| `emails.starredFirst` | bool | true | Prioritize starred/flagged emails |
| `emails.unreadOnly` | bool | true | Only show unread emails |

### Important Emails Feature

The important emails feature uses AI-powered semantic analysis to identify actionable emails:

- **Order shipments and tracking**
- **Receipts and transaction confirmations**
- **Banking and payment alerts**
- **Account security notices**
- **Subscription renewals**
- **Calendar invites**
- **Messages from your contacts**
- **Job-related communications**

**To enable:**

1. Set `emails.enabled` to `true` in your config
2. For iCloud Mail: Generate an [app-specific password](https://support.apple.com/en-us/HT204397) and set it in `emails.icloudPassword`
3. For Gmail: Ensure `gog` is authenticated

**Privacy note:** Email analysis happens locally using the agent's model context. No email data is sent to external services beyond what you've configured.

## Usage

### Interactive Mode

Simply ask your agent for your daily briefing:

```
What's my briefing for today?
```

The agent will:
1. Check if location/timezone are configured
2. Prompt for missing info if needed
3. Generate your personalized briefing

### Cron Mode

For automated delivery (e.g., via Telegram bot), the skill:
- Uses sensible defaults without prompting
- Omits weather if location is unavailable
- Always succeeds and outputs the briefing text

Example cron setup:
```bash
# Daily briefing at 7:00 AM
0 7 * * * /path/to/your/agent --skill daily-briefing >> /var/log/briefing.log
```

## Example Output

```
Good morning - Today is Saturday, February 3, 2024. Partly cloudy skies, around 45°F this afternoon, with a low around 32°F.

🎂 **Birthdays:**
• Today: Jane Doe
• Feb 5: John Smith

📅 **Today's schedule:**
• All-day: Doctor appointment
• 9:00 AM: Team standup
• 2:00 PM: Coffee with Alex

✅ **Reminders:**
• Pick up prescription
• Call mom

📧 **Emails needing attention:**
• Amazon: Your order has shipped
• Chase: Payment received
• Google Drive: Document shared with you

Take things one step at a time today—you've got this.
```

## Troubleshooting

### Weather not showing

- Ensure `location` is set in your config
- Check that `curl` can reach wttr.in: `curl -fsSL "https://wttr.in/London?format=j1"`

### Calendar events not showing

- For iCloud: Install `icalpal` and grant Calendar permission
- For Google: Ensure `gog` is authenticated: `gog auth login`

### Reminders not showing

- Install `remindctl`: `brew install steipete/tap/remindctl`
- Grant Reminders permission to Terminal.app

### Birthdays not showing

- Grant Contacts permission to Terminal.app
- For Google: Ensure `gog` is authenticated

### Emails not showing

- Set `emails.enabled` to `true` in config
- For Gmail: Ensure `gog` is authenticated
- For iCloud: Set `emails.icloudPassword` with an app-specific password

### Permission denied errors

Grant Terminal.app (or your terminal) access in System Settings > Privacy & Security for Contacts, Calendars, and Reminders.

## Dependencies

**Required:**
- `curl` — for weather fetching (built into macOS)
- `bash` — for orchestrator script (built into macOS)
- `python3` — for JSON parsing (built into macOS)

**Optional:**
- `gog` — `brew install steipete/tap/gogcli` (Google Calendar, Gmail, Contacts)
- `icalpal` — `brew install ajrosen/tap/icalpal` (iCloud Calendar)
- `remindctl` — `brew install steipete/tap/remindctl` (Apple Reminders)
- `himalaya` — `brew install himalaya` (iCloud Mail via IMAP)

## File Structure

```
daily-briefing/
├── SKILL.md                              # Skill definition
├── README.md                             # This file
├── install.sh                            # Installer script
├── scripts/
│   └── daily_briefing_orchestrator.sh    # Data gathering script
└── bin/
    └── run_daily_briefing.sh             # Runner script (created by install.sh)
```

## License

MIT


path: _meta.json
content: ownerId: kn7cenc3hxbp14kw8vayknkrzd808zvg
slug: daily-briefing
version: 1.0.4
publishedAt: 1770494510772

path: install.sh
content: #!/usr/bin/env bash
# install.sh - Installs the daily-briefing skill

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BIN_DIR="${SCRIPT_DIR}/bin"
ORCHESTRATOR="${SCRIPT_DIR}/scripts/daily_briefing_orchestrator.sh"
RUNNER_SCRIPT="${BIN_DIR}/run_daily_briefing.sh"

echo "📦 Installing daily-briefing skill..."

# Create bin directory
mkdir -p "$BIN_DIR"

# Remove old runner script if exists (from previous install approach)
rm -rf "$RUNNER_SCRIPT" 2>/dev/null || true

# Make orchestrator executable
chmod +x "$ORCHESTRATOR"

# Create runner script that simply invokes the orchestrator
cat > "$RUNNER_SCRIPT" << 'RUNNER_EOF'
#!/bin/bash
# run_daily_briefing.sh - Runs the daily briefing data gatherer
# TCC permissions are granted to Terminal.app (or the calling process)

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ORCHESTRATOR="${SCRIPT_DIR}/../scripts/daily_briefing_orchestrator.sh"

if [[ ! -f "$ORCHESTRATOR" ]]; then
  echo "Error: Orchestrator not found at $ORCHESTRATOR" >&2
  exit 1
fi

# Pass any arguments (e.g., --cleanup)
bash -l "$ORCHESTRATOR" "$@"
RUNNER_EOF

chmod +x "$RUNNER_SCRIPT"

echo "✅ Runner script created at: ${RUNNER_SCRIPT}"
echo ""
echo "📋 Next steps:"
echo ""
echo "   1. Grant Terminal.app access to Contacts, Calendar, and Reminders:"
echo "      System Settings > Privacy & Security > [Contacts/Calendars/Reminders]"
echo "      Add: Terminal (or your terminal app)"
echo ""
echo "   2. Test the skill:"
echo "      ${RUNNER_SCRIPT}"
echo "      cat /tmp/daily_briefing_data.json"
echo ""
echo "   3. Install optional CLI tools for enhanced data:"
echo "      brew install steipete/tap/gogcli      # Google services"
echo "      brew install ajrosen/tap/icalpal      # iCloud Calendar"
echo "      brew install steipete/tap/remindctl   # Apple Reminders"
echo "      brew install himalaya                 # iCloud Mail"
echo ""
echo "   4. Configure the skill in ~/.openclaw/openclaw.json"
echo ""
echo "🎉 Installation complete!"


path: scripts/daily_briefing_orchestrator.sh
content: #!/usr/bin/env bash
# daily_briefing_orchestrator.sh
# Gathers data for the daily-briefing skill via TCC-safe execution.

set -euo pipefail

# PATH setup for .app environment
export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:$PATH"

# Unique per-run file for atomic writes
RUN_ID="$(date +%s).$$.$RANDOM"
OUT="/tmp/daily_briefing_data.${RUN_ID}.json"
CANON="/tmp/daily_briefing_data.json"

# Cleanup handler
if [[ "${1:-}" == "--cleanup" ]]; then
  rm -f /tmp/daily_briefing_data.*.json /tmp/daily_briefing_data.json 2>/dev/null || true
  exit 0
fi

# Helper: check if command exists
has_cmd() {
  command -v "$1" &>/dev/null
}

# Helper: safely output JSON value (escapes strings)
json_escape() {
  python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().strip()))' 2>/dev/null || echo '""'
}

# Read configuration from ~/.openclaw/openclaw.json
CONFIG_FILE="$HOME/.openclaw/openclaw.json"
if [[ -f "$CONFIG_FILE" ]]; then
  CONFIG_JSON=$(cat "$CONFIG_FILE" 2>/dev/null || echo '{}')
else
  CONFIG_JSON='{}'
fi

# Extract config values using python3 (available on macOS)
get_config() {
  local path="$1"
  local default="$2"
  python3 -c "
import json, sys
path = sys.argv[1]
default = sys.argv[2]
json_str = sys.argv[3]
try:
    data = json.loads(json_str)
    keys = path.split('.')
    val = data
    for k in keys:
        if isinstance(val, dict):
            val = val.get(k)
        else:
            val = None
        if val is None:
            break
    if val is None:
        print(default)
    elif isinstance(val, bool):
        print('true' if val else 'false')
    elif isinstance(val, list):
        print(json.dumps(val))
    else:
        print(val)
except:
    print(default)
" "$path" "$default" "$CONFIG_JSON"
}

# Get configuration values
LOCATION=$(get_config "skills.entries.daily-briefing.config.location" "")
TIMEZONE=$(get_config "skills.entries.daily-briefing.config.timezone" "")
UNITS=$(get_config "skills.entries.daily-briefing.config.units" "C")
BIRTHDAYS_ENABLED=$(get_config "skills.entries.daily-briefing.config.birthdays.enabled" "false")
BIRTHDAYS_LOOKAHEAD=$(get_config "skills.entries.daily-briefing.config.birthdays.lookahead" "14")
BIRTHDAYS_SOURCES=$(get_config "skills.entries.daily-briefing.config.birthdays.sources" '["contacts"]')
CALENDAR_ENABLED=$(get_config "skills.entries.daily-briefing.config.calendar.enabled" "false")
CALENDAR_LOOKAHEAD=$(get_config "skills.entries.daily-briefing.config.calendar.lookahead" "0")
CALENDAR_SOURCES=$(get_config "skills.entries.daily-briefing.config.calendar.sources" '["google","icloud"]')
REMINDERS_ENABLED=$(get_config "skills.entries.daily-briefing.config.reminders.enabled" "false")
REMINDERS_DUE_FILTER=$(get_config "skills.entries.daily-briefing.config.reminders.dueFilter" "today")
REMINDERS_INCLUDE_PAST_DUE=$(get_config "skills.entries.daily-briefing.config.reminders.includePastDue" "true")
EMAILS_ENABLED=$(get_config "skills.entries.daily-briefing.config.emails.enabled" "false")
EMAILS_ICLOUD_PASSWORD=$(get_config "skills.entries.daily-briefing.config.emails.icloudPassword" "")
EMAILS_LIMIT=$(get_config "skills.entries.daily-briefing.config.emails.limit" "10")
EMAILS_SORT_NEWEST=$(get_config "skills.entries.daily-briefing.config.emails.sortNewest" "true")
EMAILS_STARRED_FIRST=$(get_config "skills.entries.daily-briefing.config.emails.starredFirst" "true")
EMAILS_UNREAD_ONLY=$(get_config "skills.entries.daily-briefing.config.emails.unreadOnly" "true")

# Get user name for birthday matching
USER_FULL_NAME=$(get_config "user.name" "")
if [[ -z "$USER_FULL_NAME" ]]; then
  USER_FULL_NAME=$(id -F 2>/dev/null || echo "")
fi

# Get timezone if not configured
if [[ -z "$TIMEZONE" ]]; then
  TIMEZONE=$(readlink /etc/localtime 2>/dev/null | sed 's|.*/zoneinfo/||' || echo "UTC")
fi

# Get current time info
# Remove leading zero from hour to ensure valid JSON number format
CURRENT_HOUR=$(TZ="$TIMEZONE" date +%H 2>/dev/null || date +%H)
CURRENT_HOUR=$((10#$CURRENT_HOUR))
LOCAL_TIME=$(TZ="$TIMEZONE" date +%Y-%m-%dT%H:%M:%S 2>/dev/null || date +%Y-%m-%dT%H:%M:%S)
TODAY=$(TZ="$TIMEZONE" date +%Y-%m-%d 2>/dev/null || date +%Y-%m-%d)

# Calculate end date for calendar lookahead
if [[ "$CALENDAR_LOOKAHEAD" -gt 0 ]]; then
  CALENDAR_END_DATE=$(TZ="$TIMEZONE" date -v+${CALENDAR_LOOKAHEAD}d +%Y-%m-%d 2>/dev/null || date -d "+${CALENDAR_LOOKAHEAD} days" +%Y-%m-%d 2>/dev/null || echo "$TODAY")
else
  CALENDAR_END_DATE="$TODAY"
fi

# Begin JSON output
# Note: Using block redirection instead of 'exec' to avoid EBADF errors
# when spawned from Node.js/Electron environments
{

echo "{"
echo "\"generated_at\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\","

# ─────────────────────────────────────────────────────────────────────────────
# SYSTEM INFO
# ─────────────────────────────────────────────────────────────────────────────
echo "\"system\": {"
echo "  \"timezone\": \"$TIMEZONE\","
echo "  \"local_time\": \"$LOCAL_TIME\","
echo "  \"hour\": $CURRENT_HOUR"
echo "},"

# ─────────────────────────────────────────────────────────────────────────────
# CONFIG (for agent reference)
# ─────────────────────────────────────────────────────────────────────────────
echo "\"config\": {"
echo "  \"location\": \"$LOCATION\","
echo "  \"units\": \"$UNITS\","
echo "  \"birthdays_enabled\": $BIRTHDAYS_ENABLED,"
echo "  \"birthdays_lookahead\": $BIRTHDAYS_LOOKAHEAD,"
echo "  \"calendar_enabled\": $CALENDAR_ENABLED,"
echo "  \"calendar_lookahead\": $CALENDAR_LOOKAHEAD,"
echo "  \"reminders_enabled\": $REMINDERS_ENABLED,"
echo "  \"reminders_due_filter\": \"$REMINDERS_DUE_FILTER\","
echo "  \"reminders_include_past_due\": $REMINDERS_INCLUDE_PAST_DUE,"
echo "  \"emails_enabled\": $EMAILS_ENABLED,"
echo "  \"emails_limit\": $EMAILS_LIMIT,"
echo "  \"emails_sort_newest\": $EMAILS_SORT_NEWEST,"
echo "  \"emails_starred_first\": $EMAILS_STARRED_FIRST,"
echo "  \"emails_unread_only\": $EMAILS_UNREAD_ONLY"
echo "},"

# ─────────────────────────────────────────────────────────────────────────────
# BIRTHDAYS
# ─────────────────────────────────────────────────────────────────────────────
echo "\"birthdays\": {"
if [[ "$BIRTHDAYS_ENABLED" == "true" ]]; then
  BIRTHDAY_DATA="[]"
  USER_BDAY_TODAY="false"
  
  # Check if contacts source is enabled
  if echo "$BIRTHDAYS_SOURCES" | grep -q "contacts"; then
    # Get birthdays from Contacts.app (iCloud)
    CONTACTS_BIRTHDAYS=$(osascript -l JavaScript -e "
const app = Application('Contacts');
const today = new Date();
const lookahead = $BIRTHDAYS_LOOKAHEAD;
const results = [];
const userNameLower = '$USER_FULL_NAME'.toLowerCase();

try {
  const people = app.people();
  for (const person of people) {
    try {
      const bday = person.birthDate();
      if (bday) {
        const thisYear = new Date(today.getFullYear(), bday.getMonth(), bday.getDate());
        const nextYear = new Date(today.getFullYear() + 1, bday.getMonth(), bday.getDate());
        const upcoming = thisYear >= new Date(today.getFullYear(), today.getMonth(), today.getDate()) ? thisYear : nextYear;
        const todayMidnight = new Date(today.getFullYear(), today.getMonth(), today.getDate());
        const daysUntil = Math.floor((upcoming - todayMidnight) / (1000 * 60 * 60 * 24));
        
        if (daysUntil >= 0 && daysUntil <= lookahead) {
          const name = person.name();
          results.push({
            name: name,
            date: upcoming.toISOString().split('T')[0],
            days_until: daysUntil,
            source: 'contacts'
          });
        }
      }
    } catch (e) {}
  }
} catch (e) {}

JSON.stringify(results.sort((a, b) => a.days_until - b.days_until));
" 2>/dev/null || echo "[]")
    
    if [[ "$CONTACTS_BIRTHDAYS" != "[]" ]]; then
      BIRTHDAY_DATA="$CONTACTS_BIRTHDAYS"
    fi
  fi
  
  # Check if google source is enabled and gog is available
  if echo "$BIRTHDAYS_SOURCES" | grep -q "google" && has_cmd gog; then
    # Get birthdays from Google Contacts
    GOG_CONTACTS=$(gog contacts list --json 2>/dev/null || echo "[]")
    GOOGLE_BIRTHDAYS=$(echo "$GOG_CONTACTS" | python3 -c "
import json, sys
from datetime import datetime, timedelta

try:
    contacts = json.load(sys.stdin)
    today = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
    lookahead = $BIRTHDAYS_LOOKAHEAD
    results = []
    
    for contact in contacts:
        bday_str = contact.get('birthday', '')
        name = contact.get('name', '')
        if bday_str and name:
            try:
                # Try different date formats
                for fmt in ['%Y-%m-%d', '%m/%d/%Y', '%d/%m/%Y']:
                    try:
                        bday = datetime.strptime(bday_str, fmt)
                        break
                    except:
                        continue
                else:
                    continue
                
                this_year = bday.replace(year=today.year)
                next_year = bday.replace(year=today.year + 1)
                upcoming = this_year if this_year >= today else next_year
                days_until = (upcoming - today).days
                
                if 0 <= days_until <= lookahead:
                    results.append({
                        'name': name,
                        'date': upcoming.strftime('%Y-%m-%d'),
                        'days_until': days_until,
                        'source': 'google'
                    })
            except:
                pass
    
    print(json.dumps(sorted(results, key=lambda x: x['days_until'])))
except:
    print('[]')
" 2>/dev/null || echo "[]")
    
    # Merge with existing birthday data
    if [[ "$GOOGLE_BIRTHDAYS" != "[]" ]]; then
      BIRTHDAY_DATA=$(echo "$BIRTHDAY_DATA" "$GOOGLE_BIRTHDAYS" | python3 -c "
import json, sys
data = []
for line in sys.stdin:
    try:
        data.extend(json.loads(line.strip()))
    except:
        pass
# Deduplicate by name+date
seen = set()
unique = []
for item in sorted(data, key=lambda x: x['days_until']):
    key = (item['name'].lower(), item['date'])
    if key not in seen:
        seen.add(key)
        unique.append(item)
print(json.dumps(unique))
" 2>/dev/null || echo "$BIRTHDAY_DATA")
    fi
  fi
  
  # Check if user's birthday is today
  USER_BDAY_TODAY=$(echo "$BIRTHDAY_DATA" | python3 -c "
import json, sys
try:
    data = json.load(sys.stdin)
    user_name = '$USER_FULL_NAME'.lower()
    for item in data:
        if item.get('days_until', -1) == 0 and user_name and user_name in item.get('name', '').lower():
            print('true')
            sys.exit(0)
    print('false')
except:
    print('false')
" 2>/dev/null || echo "false")
  
  echo "  \"available\": true,"
  echo "  \"user_birthday_today\": $USER_BDAY_TODAY,"
  echo "  \"data\": $BIRTHDAY_DATA"
else
  echo "  \"available\": false"
fi
echo "},"

# ─────────────────────────────────────────────────────────────────────────────
# CALENDAR EVENTS
# ─────────────────────────────────────────────────────────────────────────────
echo "\"calendar\": {"
if [[ "$CALENDAR_ENABLED" == "true" ]]; then
  CALENDAR_DATA="[]"
  
  # Check if google source is enabled and gog is available
  if echo "$CALENDAR_SOURCES" | grep -q "google" && has_cmd gog; then
    GOOGLE_EVENTS=$(gog calendar events --from "$TODAY" --to "$CALENDAR_END_DATE" --json 2>/dev/null || echo '{"events":[]}')
    GOOGLE_PARSED=$(echo "$GOOGLE_EVENTS" | python3 -c "
import json, sys
try:
    data = json.load(sys.stdin)
    # gog returns {\"events\": [...], \"nextPageToken\": \"...\"}
    events = data.get('events', []) if isinstance(data, dict) else data
    results = []
    for event in events:
        if event.get('status') == 'confirmed':
            start = event.get('start', {})
            end = event.get('end', {})
            all_day = 'date' in start and 'dateTime' not in start
            
            if all_day:
                start_time = None
                end_time = None
                date = start.get('date', '')
            else:
                start_dt = start.get('dateTime', '')
                end_dt = end.get('dateTime', '')
                start_time = start_dt.split('T')[1][:5] if 'T' in start_dt else None
                end_time = end_dt.split('T')[1][:5] if 'T' in end_dt else None
                date = start_dt.split('T')[0] if 'T' in start_dt else ''
            
            results.append({
                'title': event.get('summary', 'Untitled'),
                'start': start_time,
                'end': end_time,
                'all_day': all_day,
                'date': date,
                'source': 'google'
            })
    print(json.dumps(results))
except:
    print('[]')
" 2>/dev/null || echo "[]")
    
    if [[ "$GOOGLE_PARSED" != "[]" ]]; then
      CALENDAR_DATA="$GOOGLE_PARSED"
    fi
  fi
  
  # Check if icloud source is enabled and icalpal is available
  if echo "$CALENDAR_SOURCES" | grep -q "icloud" && has_cmd icalpal; then
    if [[ "$CALENDAR_LOOKAHEAD" -gt 0 ]]; then
      ICAL_EVENTS=$(icalpal --is=iCloud -o json "eventsToday+${CALENDAR_LOOKAHEAD}" 2>/dev/null || echo "[]")
    else
      ICAL_EVENTS=$(icalpal --is=iCloud -o json eventsToday 2>/dev/null || echo "[]")
    fi
    
    ICAL_PARSED=$(echo "$ICAL_EVENTS" | python3 -c "
import json, sys
from datetime import datetime
try:
    events = json.load(sys.stdin)
    results = []
    for event in events:
        all_day = event.get('all_day', 0) == 1
        start_secs = event.get('sseconds', 0)
        end_secs = event.get('eseconds', 0)
        
        if all_day:
            start_time = None
            end_time = None
        else:
            start_time = datetime.fromtimestamp(start_secs).strftime('%H:%M') if start_secs else None
            end_time = datetime.fromtimestamp(end_secs).strftime('%H:%M') if end_secs else None
        
        date = event.get('sdate', '') or datetime.fromtimestamp(start_secs).strftime('%Y-%m-%d') if start_secs else ''
        
        results.append({
            'title': event.get('title', 'Untitled'),
            'start': start_time,
            'end': end_time,
            'all_day': all_day,
            'date': date,
            'source': 'icloud',
            'sort_key': 0 if all_day else start_secs
        })
    
    # Sort: all-day first, then by start time
    results.sort(key=lambda x: (0 if x['all_day'] else 1, x.get('sort_key', 0)))
    for r in results:
        r.pop('sort_key', None)
    print(json.dumps(results))
except:
    print('[]')
" 2>/dev/null || echo "[]")
    
    # Merge with existing calendar data
    if [[ "$ICAL_PARSED" != "[]" ]]; then
      CALENDAR_DATA=$(echo "$CALENDAR_DATA" "$ICAL_PARSED" | python3 -c "
import json, sys
data = []
for line in sys.stdin:
    try:
        data.extend(json.loads(line.strip()))
    except:
        pass
# Sort: by date, then all-day first, then by start time
def sort_key(x):
    date = x.get('date', '9999-99-99')
    all_day = 0 if x.get('all_day') else 1
    start = x.get('start') or '00:00'
    return (date, all_day, start)
data.sort(key=sort_key)
print(json.dumps(data))
" 2>/dev/null || echo "$CALENDAR_DATA")
    fi
  fi
  
  echo "  \"available\": true,"
  echo "  \"data\": $CALENDAR_DATA"
else
  echo "  \"available\": false"
fi
echo "},"

# ─────────────────────────────────────────────────────────────────────────────
# REMINDERS
# ─────────────────────────────────────────────────────────────────────────────
echo "\"reminders\": {"
if [[ "$REMINDERS_ENABLED" == "true" ]] && has_cmd remindctl; then
  # Build remindctl command based on config
  REMINDCTL_ARGS="list --json"
  if [[ -n "$REMINDERS_DUE_FILTER" && "$REMINDERS_DUE_FILTER" != "all" ]]; then
    REMINDCTL_ARGS="$REMINDCTL_ARGS --due $REMINDERS_DUE_FILTER"
  fi
  
  # Get reminders with due filter
  REMINDERS_RAW=$(remindctl $REMINDCTL_ARGS 2>/dev/null || echo "[]")
  
  # If including past due, also fetch overdue reminders and merge
  if [[ "$REMINDERS_INCLUDE_PAST_DUE" == "true" ]]; then
    PAST_DUE_RAW=$(remindctl list --overdue --json 2>/dev/null || echo "[]")
    REMINDERS_RAW=$(python3 -c "
import json, sys
try:
    current = json.loads(sys.argv[1])
    past_due = json.loads(sys.argv[2])
    # Merge and deduplicate by id
    seen_ids = set()
    merged = []
    for r in past_due + current:
        rid = r.get('id', r.get('title', ''))
        if rid not in seen_ids:
            seen_ids.add(rid)
            merged.append(r)
    print(json.dumps(merged))
except:
    print(sys.argv[1])
" "$REMINDERS_RAW" "$PAST_DUE_RAW" 2>/dev/null || echo "$REMINDERS_RAW")
  fi
  
  REMINDERS_DATA=$(echo "$REMINDERS_RAW" | python3 -c "
import json, sys
try:
    reminders = json.load(sys.stdin)
    results = []
    for r in reminders:
        if not r.get('completed', False):
            results.append({
                'title': r.get('title', 'Untitled'),
                'due': r.get('dueDate', '')
            })
    print(json.dumps(results[:10]))  # Limit to 10
except:
    print('[]')
" 2>/dev/null || echo "[]")
  
  echo "  \"available\": true,"
  echo "  \"data\": $REMINDERS_DATA"
else
  echo "  \"available\": false"
fi
echo "},"

# ─────────────────────────────────────────────────────────────────────────────
# EMAILS
# ─────────────────────────────────────────────────────────────────────────────
echo "\"emails\": {"
if [[ "$EMAILS_ENABLED" == "true" ]]; then
  EMAIL_DATA="[]"
  
  # Gmail via gog
  if has_cmd gog; then
    GMAIL_RAW=$(gog mail search "in:inbox" --json 2>/dev/null || echo "{}")
    GMAIL_PARSED=$(echo "$GMAIL_RAW" | python3 -c "
import json, sys
try:
    data = json.load(sys.stdin)
    threads = data.get('threads', []) if isinstance(data, dict) else data
    results = []
    for t in threads[:50]:  # Limit initial fetch
        from_str = t.get('from', '')
        # Parse 'Name <email>' format
        if '<' in from_str and '>' in from_str:
            name = from_str.split('<')[0].strip().strip('\"')
            email = from_str.split('<')[1].rstrip('>')
        else:
            name = from_str
            email = from_str
        labels = t.get('labels', [])
        results.append({
            'id': t.get('id', ''),
            'from': name,
            'from_email': email,
            'subject': t.get('subject', ''),
            'preview': t.get('snippet', '')[:200] if t.get('snippet') else '',
            'starred': 'STARRED' in labels,
            'unread': 'UNREAD' in labels,
            'date': t.get('date', ''),
            'source': 'gmail'
        })
    print(json.dumps(results))
except:
    print('[]')
" 2>/dev/null || echo "[]")
    
    if [[ "$GMAIL_PARSED" != "[]" ]]; then
      EMAIL_DATA="$GMAIL_PARSED"
    fi
  fi
  
  # iCloud Mail via himalaya (only if configured)
  HIMALAYA_CONFIG="$HOME/Library/Application Support/himalaya/config.toml"
  if has_cmd himalaya && [[ -n "$EMAILS_ICLOUD_PASSWORD" ]] && [[ -f "$HIMALAYA_CONFIG" ]]; then
    HIMALAYA_RAW=$(himalaya envelope list --folder INBOX --output json 2>/dev/null || echo "[]")
    HIMALAYA_PARSED=$(echo "$HIMALAYA_RAW" | python3 -c "
import json, sys
try:
    envelopes = json.load(sys.stdin)
    results = []
    for e in envelopes[:50]:  # Limit initial fetch
        sender = e.get('from', {})
        if isinstance(sender, list) and len(sender) > 0:
            sender = sender[0]
        elif isinstance(sender, str):
            sender = {'name': sender, 'addr': sender}
        
        results.append({
            'id': str(e.get('id', '')),
            'from': sender.get('name', '') or sender.get('addr', ''),
            'from_email': sender.get('addr', ''),
            'subject': e.get('subject', ''),
            'preview': '',  # Himalaya envelope doesn't include preview
            'starred': 'flagged' in [f.lower() for f in e.get('flags', [])],
            'unread': 'seen' not in [f.lower() for f in e.get('flags', [])],
            'date': e.get('date', ''),
            'source': 'icloud'
        })
    print(json.dumps(results))
except:
    print('[]')
" 2>/dev/null || echo "[]")
    
    # Merge with existing email data
    if [[ "$HIMALAYA_PARSED" != "[]" ]]; then
      EMAIL_DATA=$(echo "$EMAIL_DATA" "$HIMALAYA_PARSED" | python3 -c "
import json, sys
data = []
for line in sys.stdin:
    try:
        data.extend(json.loads(line.strip()))
    except:
        pass
print(json.dumps(data))
" 2>/dev/null || echo "$EMAIL_DATA")
    fi
  fi
  
  echo "  \"available\": true,"
  echo "  \"data\": $EMAIL_DATA"
else
  echo "  \"available\": false"
fi
echo "},"

# ─────────────────────────────────────────────────────────────────────────────
# CONTACTS (for email importance classification)
# ─────────────────────────────────────────────────────────────────────────────
echo "\"contacts\": {"
if [[ "$EMAILS_ENABLED" == "true" ]]; then
  CONTACTS_DATA="[]"
  
  # Contacts.app (iCloud)
  ICLOUD_CONTACTS=$(osascript -l JavaScript -e "
const app = Application('Contacts');
const results = [];
try {
  const people = app.people();
  for (const person of people) {
    try {
      const name = person.name();
      const emails = person.emails();
      for (const email of emails) {
        results.push({
          name: name,
          email: email.value()
        });
      }
    } catch (e) {}
  }
} catch (e) {}
JSON.stringify(results);
" 2>/dev/null || echo "[]")
  
  if [[ "$ICLOUD_CONTACTS" != "[]" ]]; then
    CONTACTS_DATA="$ICLOUD_CONTACTS"
  fi
  
  # Google Contacts via gog
  if has_cmd gog; then
    GOG_CONTACTS=$(gog contacts list --json 2>/dev/null || echo "[]")
    GOG_PARSED=$(echo "$GOG_CONTACTS" | python3 -c "
import json, sys
try:
    contacts = json.load(sys.stdin)
    results = []
    for c in contacts:
        name = c.get('name', '')
        for email in c.get('emails', []):
            results.append({
                'name': name,
                'email': email.get('value', '') or email
            })
    print(json.dumps(results))
except:
    print('[]')
" 2>/dev/null || echo "[]")
    
    # Merge contacts
    if [[ "$GOG_PARSED" != "[]" ]]; then
      CONTACTS_DATA=$(echo "$CONTACTS_DATA" "$GOG_PARSED" | python3 -c "
import json, sys
data = []
for line in sys.stdin:
    try:
        data.extend(json.loads(line.strip()))
    except:
        pass
# Deduplicate by email
seen = set()
unique = []
for item in data:
    email = item.get('email', '').lower()
    if email and email not in seen:
        seen.add(email)
        unique.append(item)
print(json.dumps(unique))
" 2>/dev/null || echo "$CONTACTS_DATA")
    fi
  fi
  
  echo "  \"available\": true,"
  echo "  \"data\": $CONTACTS_DATA"
else
  echo "  \"available\": false"
fi
echo "},"

# MOTIVATIONAL QUOTE
# ─────────────────────────────────────────────────────────────────────────────
QUOTES=(
  "The secret of getting ahead is getting started. — Mark Twain"
  "You are never too old to set another goal or to dream a new dream. — C.S. Lewis"
  "It does not matter how slowly you go as long as you do not stop. — Confucius"
  "Start where you are. Use what you have. Do what you can. — Arthur Ashe"
  "The only way to do great work is to love what you do. — Steve Jobs"
  "Believe you can and you're halfway there. — Theodore Roosevelt"
  "Every moment is a fresh beginning. — T.S. Eliot"
  "What lies behind us and what lies before us are tiny matters compared to what lies within us. — Ralph Waldo Emerson"
  "The best time to plant a tree was 20 years ago. The second best time is now. — Chinese Proverb"
  "You don't have to be great to start, but you have to start to be great. — Zig Ziglar"
  "In the middle of difficulty lies opportunity. — Albert Einstein"
  "Act as if what you do makes a difference. It does. — William James"
  "The future belongs to those who believe in the beauty of their dreams. — Eleanor Roosevelt"
  "Success is not final, failure is not fatal: it is the courage to continue that counts. — Winston Churchill"
  "Your limitation—it's only your imagination."
  "Push yourself, because no one else is going to do it for you."
  "Great things never come from comfort zones."
  "Dream it. Wish it. Do it."
  "Don't stop when you're tired. Stop when you're done."
  "Little things make big days."
  "It's going to be hard, but hard does not mean impossible."
  "Don't wait for opportunity. Create it."
  "Sometimes later becomes never. Do it now."
  "Dream bigger. Do bigger."
  "The harder you work for something, the greater you'll feel when you achieve it."
)
RANDOM_INDEX=$((RANDOM % ${#QUOTES[@]}))
RANDOM_QUOTE="${QUOTES[$RANDOM_INDEX]}"
# Properly escape for JSON using python3
ESCAPED_QUOTE=$(echo "$RANDOM_QUOTE" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().strip()))' 2>/dev/null || echo '"Have a wonderful day!"')
echo "\"quote\": $ESCAPED_QUOTE"

echo "}"

} >"$OUT" 2>/dev/null

# Atomic publish
cp -f "$OUT" "${CANON}.tmp" && mv -f "${CANON}.tmp" "$CANON"


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: suspicious | SkillSpector: suspicious (CRITICAL)