Create, list, and cancel reminders and cron jobs scoped to the channel they were requested from. Use when: user says 'remind me', 'set an alarm', 'schedule a...
---
name: remind-me
description: "Create, list, and cancel reminders and cron jobs scoped to the channel they were requested from. Use when: user says 'remind me', 'set an alarm', 'schedule a cron', 'alert me when', 'every day at X do Y', 'cancel my reminder', 'list my reminders'. Auto-detects source channel, chat ID, and user timezone. Delivers back to originating chat. Asks for clarification if schedule or intent is ambiguous before creating anything."
metadata: {"clawdbot":{"emoji":"⏰","requires":{"bins":["uv","openclaw"]}}}
---
# Skill: Remind Me
## When to use
- User says "remind me to...", "set a reminder", "set an alarm"
- User says "every Monday at 9 AM...", "every 30 minutes check..."
- User says "in 20 minutes tell me...", "at 3 PM send me..."
- User wants to schedule a recurring cron job via chat
- User says "cancel my reminder", "delete that cron", "stop the alert"
- User says "list my reminders", "what reminders do I have?"
- User wants to know what cron jobs are active in this chat
## When NOT to use
- User wants to check a stock price right now (not scheduled) → use `stock-price-checker-pro`
- User wants to run a one-time task immediately (no scheduling involved)
---
## ⚠️ CRITICAL: Always resolve these FOUR things before creating a reminder
Before calling the script, you MUST have all four resolved:
| Field | Question to answer | Example |
|---|---|---|
| **WHAT** | What should happen / be said? | "Check NVDA stock price" |
| **WHEN / HOW OFTEN** | One-time or recurring? At what time/interval? | "Every Monday at 9 AM" |
| **WHERE** | Which channel + chat ID to deliver to? | Auto-detected from session |
| **TIMEZONE** | What timezone should times be interpreted in? | Auto-detected from USER.md |
### Missing field rules
- **WHAT is missing** → Ask: "What would you like me to remind you about?"
- **WHEN is missing AND cannot be reasonably assumed** → Ask: "How often, or at what time?"
- **WHEN is missing BUT can be reasonably assumed as once** → Assume one-shot, but confirm: "Just once, right?"
- **WHERE is always auto-detected** → Never ask the user for this. Read it from session context (see below).
- **TIMEZONE is always auto-detected** → Never ask unless unresolvable (see Step 2 below).
### Do NOT create the job until all four are confirmed.
---
## Step 1 — Auto-detect channel, chat ID, and timezone from context
### Channel and chat ID
The source channel and chat ID are available in your session context. Extract them before doing anything else.
- **channel**: the platform the message arrived on (e.g. `telegram`, `discord`)
- **chatId / to**: the specific chat or user ID within that platform (e.g. `<chatId>`)
These two values are passed as `--channel` and `--to` to the script.
**Never ask the user for these. Never hardcode them. Always read from session context.**
### Timezone — 3-tier resolution (in priority order)
Resolve the user's timezone using this exact priority chain:
| Priority | Source | How to read it |
|---|---|---|
| 1 | **Message itself** | User says "at 9 AM London time" or "remind me at 3 PM EST" → extract from their words |
| 2 | **`USER.md`** | Read the `Timezone:` field from `/root/.openclaw/workspace/USER.md` |
| 3 | **Ask the user** | Only if tiers 1 and 2 both fail — ask once, then offer to save it |
**Rules:**
- If tier 1 applies → use it directly, no need to check USER.md
- If tier 2 applies → use it silently, do not mention it to the user
- If tier 3 triggers → ask: _"What timezone are you in? (e.g. London, New York, Lagos)"_ — then offer: _"Want me to remember your timezone for future reminders?"_ If they say yes, update `USER.md`
- The resolved timezone is passed as `tz:<IANA>` to the script
- If no timezone can be resolved at all, pass `tz:UTC` and mention it in the confirmation summary
---
## Step 2 — Parse the user's intent
From the user's natural language request, extract:
### Schedule type
Map what the user said to one of three prefixed schedule strings.
> ⚠️ When the user states a clock time (e.g. "9 AM", "3 PM", "noon"), always interpret it in their resolved timezone (from Step 1), not UTC. The `--tz` flag passed to the script ensures cron expressions fire at the correct local time.
| What user said | Schedule string to pass |
|---|---|
| "every 30 minutes" | `every:30m` |
| "every hour" | `every:1h` |
| "every day at 9 AM" | `cron:0 9 * * *` |
| "every Monday at 9 AM" | `cron:0 9 * * 1` |
| "weekdays at 8 AM" | `cron:0 8 * * 1-5` |
| "every Friday at 5 PM" | `cron:0 17 * * 5` |
| "in 20 minutes" | `at:20m` |
| "in 2 hours" | `at:2h` |
| "at 3 PM today" | `at:<computed duration from now>` |
| "once at 9:30 AM tomorrow" | `at:<computed duration from now>` |
### One-shot vs recurring
- `at:` → always one-shot (`--once` is auto-set by the script)
- `every:` or `cron:` → recurring by default
- If user says "just once" or "one time" with a `cron:` or `every:` → pass `--once`
### Job name
Generate a short, descriptive name from the user's request.
- "Remind me to check NVDA every Monday" → `"NVDA Check - Monday 9AM"`
- "Alert me in 20 minutes" → `"Alert - 20min"`
- "Grocery reminder at noon" → `"Groceries - Noon"`
### Message
The message is what the agent will say or do when the job fires.
Craft it clearly so the agent knows exactly what to do:
- "Remind me to do groceries" → `"Reminder: Time to do groceries! 🛒"`
- "Check NVDA every Monday at 9 AM" → `"Check the current NVDA stock price and send me a summary."`
- "Send me a motivational quote every morning" → `"Send me an inspiring motivational quote to start the day."`
---
## Step 3 — Clarification rules (ask before acting)
### Ask when:
1. **No schedule at all**: "Remind me to call John" → no time/frequency given
> Ask: "Sure! When would you like me to remind you — just once at a specific time, or on a recurring schedule?"
2. **Ambiguous frequency**: "Remind me often" or "check regularly"
> Ask: "How often? Every hour, every day, or something else?"
3. **Conflicting signals**: "Remind me every Monday but just once"
> Ask: "Just to confirm — should this be a one-time reminder or repeat every Monday?"
### Do NOT ask when:
- Everything is clear: "Remind me every Monday at 9 AM to check NVDA" → create immediately
- One-shot is obvious from context: "Remind me in 20 minutes" → at:20m, once
- User confirms after your clarification question → proceed immediately
### Confirmation before creating (always):
Before calling the script, summarise what you're about to set up and get a quick confirmation:
> "Got it! Here's what I'll set up:
> ⏰ **Reminder:** Check NVDA stock price
> 🔁 **Schedule:** Every Monday at 9 AM
> 🌍 **Timezone:** Africa/Lagos (UTC+1)
> 📱 **Delivered to:** This chat
>
> Shall I go ahead?"
Always include the **Timezone** line in the confirmation summary. If falling back to UTC because none was found, say so clearly:
> 🌍 **Timezone:** UTC (no timezone found — times will be in UTC)
Only proceed after user confirms.
---
## Commands
### Create a reminder
```bash
uv run /root/.openclaw/workspace/skills/remind-me/src/main.py create \
"<job name>" \
"<what to say or do>" \
"<every:duration | cron:expr | at:duration>" \
"<channel>" \
"<chatId>" \
[once] \
[tz:<IANA timezone>]
```
- `tz:` is optional but **always include it** when a timezone was resolved (tiers 1–2)
- Pass `tz:UTC` explicitly when falling back — makes the stored description accurate
- Examples: `tz:Africa/Lagos`, `tz:America/New_York`, `tz:Europe/London`, `tz:Asia/Kolkata`
- Common short names are accepted: `tz:EST`, `tz:IST`, `tz:CET`, `tz:Lagos`, `tz:London`
### List reminders for this chat
```bash
uv run /root/.openclaw/workspace/skills/remind-me/src/main.py list \
"<channel>" \
"<chatId>"
```
### Cancel a reminder by name
```bash
uv run /root/.openclaw/workspace/skills/remind-me/src/main.py cancel name "<job name>"
```
### Cancel a reminder by ID
```bash
uv run /root/.openclaw/workspace/skills/remind-me/src/main.py cancel id "<job id>"
```
---
## Examples
### Example 1 — Clear request, no clarification needed
**User:** "Remind me every Monday at 9 AM to check NVDA"
1. Detect: channel=telegram, to=<chatId>
2. Read USER.md → Timezone: Africa/Lagos → resolves to `Africa/Lagos`
3. Parse: WHAT="Check NVDA stock price", WHEN=cron:0 9 * * 1, one-shot=false
4. Confirm with user (include timezone line)
5. Run:
```bash
uv run /root/.openclaw/workspace/skills/remind-me/src/main.py create \
"NVDA Check - Monday 9AM" \
"Check the current NVDA stock price and send me a summary." \
"cron:0 9 * * 1" \
"telegram" \
"<chatId>" \
"tz:Africa/Lagos"
```
---
### Example 2 — Missing frequency, ask first
**User:** "Remind me to go do groceries by 12 PM"
1. Detect: channel=telegram, to=<chatId>
2. Read USER.md → Timezone: Africa/Lagos → resolves to `Africa/Lagos`
3. Parse: WHAT="Do groceries", WHEN=12 PM but frequency unclear
4. Ask: "Just to confirm — is this a one-time reminder for today at noon, or should I remind you every day at 12 PM?"
5. User says: "Just today"
6. Compute duration from now to today's noon **in Africa/Lagos time** → e.g. `at:3h30m`
7. Confirm (include timezone line), then run:
```bash
uv run /root/.openclaw/workspace/skills/remind-me/src/main.py create \
"Groceries - Noon" \
"Reminder: Time to go do groceries! 🛒" \
"at:3h30m" \
"telegram" \
"<chatId>" \
once \
"tz:Africa/Lagos"
```
---
### Example 3 — List reminders
**User:** "What reminders do I have?"
```bash
uv run /root/.openclaw/workspace/skills/remind-me/src/main.py list \
"telegram" \
"<chatId>"
```
Format the output as a readable list, not raw JSON. Example response:
> You have **2 active reminders** in this chat:
>
> 1. **NVDA Check - Monday 9AM** 🔁 Every Monday at 9 AM
> _"Check the current NVDA stock price..."_
> 🌍 Timezone: Africa/Lagos (UTC+1)
> Next run: Mon 10 Mar 2026, 09:00
>
> 2. **Groceries - Noon** (one-time)
> _"Time to go do groceries!"_
> 🌍 Timezone: Africa/Lagos (UTC+1)
> Runs in: 3h 30m
---
### Example 4 — Cancel a reminder
**User:** "Cancel my NVDA reminder"
1. List jobs for this channel/chat first (internally)
2. Find the matching job by name
3. Confirm: "Cancel **NVDA Check - Monday 9AM**? This will stop all future runs."
4. User confirms
5. Run:
```bash
uv run /root/.openclaw/workspace/skills/remind-me/src/main.py cancel name "NVDA Check - Monday 9AM"
```
---
### Example 5 — One-shot in-chat reminder
**User:** "In 30 seconds send me a love letter"
> `at:` schedules are duration-based (relative to now), so timezone does not affect when they fire.
> Still pass `tz:` so it is stored in the job description for consistency.
```bash
uv run /root/.openclaw/workspace/skills/remind-me/src/main.py create \
"Love Letter - 30s" \
"Write a beautiful, heartfelt love letter. Make it romantic and touching." \
"at:30s" \
"telegram" \
"<chatId>" \
"tz:Africa/Lagos"
```
---
### Example 6 — Timezone in message, no USER.md entry
**User:** "Remind me every day at 8 AM New York time to drink water"
1. Detect: channel=telegram, to=<chatId>
2. Tier 1 hit: "New York time" → resolves to `America/New_York` — no need to check USER.md
3. Parse: WHAT="Drink water", WHEN=cron:0 8 * * *, one-shot=false
4. Confirm with user
5. Run:
```bash
uv run /root/.openclaw/workspace/skills/remind-me/src/main.py create \
"Drink Water - 8AM NY" \
"Reminder: Time to drink water! 💧" \
"cron:0 8 * * *" \
"telegram" \
"<chatId>" \
"tz:America/New_York"
```
---
### Example 7 — No timezone anywhere, ask user
**User:** "Remind me every day at 7 AM to exercise"
1. Detect: channel=telegram, to=<chatId>
2. Tier 1: no timezone in message
3. Tier 2: USER.md Timezone field is empty
4. Tier 3: Ask — _"What timezone are you in? (e.g. London, Lagos, New York)"_
5. User says: "Lagos"
6. Resolve: `Africa/Lagos`
7. Offer: _"Want me to remember Lagos time for future reminders?"_
8. If yes → update USER.md: `Timezone: Africa/Lagos`
9. Confirm, then run:
```bash
uv run /root/.openclaw/workspace/skills/remind-me/src/main.py create \
"Exercise - 7AM" \
"Reminder: Time to exercise! 💪" \
"cron:0 7 * * *" \
"telegram" \
"<chatId>" \
"tz:Africa/Lagos"
```
---
## Channel scoping rules
- Jobs created from **Group A** are only visible when listing from **Group A**
- Jobs created from **Group B** are only visible when listing from **Group B**
- Jobs created from the **main/private chat** are visible only in that main chat
- The script handles this automatically via the `[remind-me:channel:chatId]` tag embedded in each job's description
- **Never show a user jobs that belong to a different chat**
---
## Output formatting
After running any command, always format the result in plain conversational language — never dump raw JSON to the user.
### On create success:
> ✅ Done! I've set up your reminder:
> ⏰ **NVDA Check** — Every Monday at 9 AM
> 📱 Delivered to this chat
### On list (empty):
> You have no active reminders in this chat.
### On cancel success:
> ✅ Reminder **"NVDA Check - Monday 9AM"** has been cancelled.
### On error:
> ❌ Something went wrong: `<error message>`
> Want me to try again?
---
## Notes
- `uv run` auto-installs dependencies from the inline script header — no pip or venv needed.
- The script calls `openclaw cron` CLI internally — the gateway must be running.
- Always use `--channel "last"` behaviour naturally: since `--to` is set to the originating chat ID, delivery is always back to the right place.
- Do NOT use sessions_spawn, web search, or any other tool to create cron jobs — always go through this script.
- Do NOT hardcode channel IDs. Always read from session context.
- The `at:` schedule prefix does not support standard cron expressions — use `cron:` for those.don't have the plugin yet? install it then click "run inline in claude" again.
create, list, and cancel reminders and cron jobs scoped to individual channels. use this when a user says "remind me", "set an alarm", "schedule a cron", "alert me when", "every day at X do Y", "cancel my reminder", or "list my reminders". the skill auto-detects the originating channel, chat ID, and user timezone, then delivers the reminder back to that same chat. asks for clarification if the schedule or intent is ambiguous before committing.
| input | type | source | required | notes |
|---|---|---|---|---|
| channel | string | session context | yes | platform identifier (e.g. telegram, discord, slack). extract from current message metadata, never ask user. |
| chatId / to | string | session context | yes | unique chat or user ID within the platform (e.g. <chatId>). extract from current message metadata, never hardcode. |
| user timezone | string | tier-1: message text, tier-2: /root/.openclaw/workspace/USER.md Timezone field, tier-3: ask user |
yes | IANA timezone code (e.g. Africa/Lagos, America/New_York) or common shorthand (e.g. EST, IST). resolve in priority order. if all tiers fail, default to UTC and mention it explicitly. |
| job name | string | derived from user intent | yes | short, descriptive slug (e.g. "NVDA Check - Monday 9AM"). generate from the user's request. |
| message / action | string | user's intent | yes | what the agent will say or do when the job fires (e.g. "Check the current NVDA stock price and send me a summary."). be explicit. |
| schedule expression | string | parsed from user's natural language | yes | one of: every:<duration> (e.g. every:30m), cron:<cron-expr> (e.g. cron:0 9 * * 1), or at:<duration> (e.g. at:20m). infer from user's words. |
| once flag | boolean | user clarification | no | pass once to make a recurring schedule (cron/every) fire one time only. omit for recurring jobs or for at: (which is always one-shot). |
| openclaw gateway | running service | infrastructure | yes | script calls openclaw cron CLI internally. gateway must be running. |
1a. Extract channel and chatId from the current message's session context (metadata). these are always available and must never be hardcoded or asked.
1b. Resolve the user's timezone using the three-tier priority chain:
/root/.openclaw/workspace/USER.md and check the Timezone: field. if present, use it silently (do not mention it to the user).UTC and state it clearly in the confirmation summary.output of step 1: channel, chatId, resolved_tz (IANA code or shorthand).
2a. From the user's natural language request, extract the WHAT (action/message), WHEN (schedule), WHERE (channel/chat, auto-detected in step 1), and TIMEZONE (from step 1).
2b. Map the user's schedule language to a schedule expression:
every:30mevery:1hcron:0 9 * * * (0 hour in resolved timezone)cron:0 9 * * 1cron:0 8 * * 1-5cron:0 17 * * 5 (17 hour in 24-hour format, resolved timezone)at:20mat:2hat:<duration> (e.g. at:4h)at:<duration>critical rule: when the user states a clock time (9 AM, 3 PM, noon), always interpret it in their resolved timezone from step 1. the --tz flag passed to the script ensures cron expressions fire at the correct local time.
2c. Determine one-shot vs recurring:
at: prefix → always one-shot (script sets --once automatically).every: or cron: prefix → recurring by default.once flag.2d. Generate a short, descriptive job name:
"NVDA Check - Monday 9AM""Alert - 20min""Groceries - Noon"2e. Craft the message/action (what the agent will say or do):
"Reminder: Time to do groceries! 🛒""Check the current NVDA stock price and send me a summary.""Send me an inspiring motivational quote to start the day."output of step 2: job_name, message, schedule_expr, is_once (boolean).
3a. Determine if clarification is needed by checking these conditions:
no schedule at all: user says "Remind me to call John" (no time or frequency). ask: "Sure! When would you like me to remind you , just once at a specific time, or on a recurring schedule?"
ambiguous frequency: user says "Remind me often" or "check regularly" (too vague). ask: "How often? Every hour, every day, or something else?"
conflicting signals: user says "Remind me every Monday but just once" (contradictory). ask: "Just to confirm , should this be a one-time reminder or repeat every Monday?"
3b. Do NOT ask when:
output of step 3: either abort and ask a clarification question, or proceed to step 4 with job_name, message, schedule_expr, is_once confirmed.
4a. Summarize what you are about to set up. always include the timezone line. example:
Got it! Here's what I'll set up: ⏰ Reminder: Check NVDA stock price 🔁 Schedule: Every Monday at 9 AM 🌍 Timezone: Africa/Lagos (UTC+1) 📱 Delivered to: This chat
Shall I go ahead?
4b. If timezone resolution fell back to UTC (no user timezone found), write:
🌍 Timezone: UTC (no timezone found , times will be in UTC)
4c. Wait for user confirmation before calling the script. only proceed if user affirms.
output of step 4: user confirmation, or request to modify/cancel.
5a. Call the script with all parameters:
uv run /root/.openclaw/workspace/skills/remind-me/src/main.py create \
"<job_name>" \
"<message>" \
"<schedule_expr>" \
"<channel>" \
"<chatId>" \
[once] \
[tz:<resolved_tz>]
rules for the call:
tz: is optional but always include it when a timezone was resolved in step 1 (tiers 1 or 2). pass tz:UTC explicitly when falling back to UTC.tz:Africa/Lagos, tz:America/New_York, tz:Europe/London, tz:Asia/Kolkata, tz:EST, tz:IST, tz:CET.once as a bare flag (no value) if is_once is true.5b. On success, format output conversationally:
✅ Done! I've set up your reminder: ⏰ NVDA Check , Every Monday at 9 AM 📱 Delivered to this chat
5c. On error, report clearly:
❌ Something went wrong:
<error message>Want me to try again?
output of step 5: success confirmation or error.
6a. Call the list command:
uv run /root/.openclaw/workspace/skills/remind-me/src/main.py list \
"<channel>" \
"<chatId>"
6b. Format output as a readable list (not raw JSON). example:
You have 2 active reminders in this chat:
NVDA Check - Monday 9AM 🔁 Every Monday at 9 AM "Check the current NVDA stock price..." 🌍 Timezone: Africa/Lagos (UTC+1) Next run: Mon 10 Mar 2026, 09:00
Groceries - Noon (one-time) "Time to go do groceries!" 🌍 Timezone: Africa/Lagos (UTC+1) Runs in: 3h 30m
6c. If no reminders exist, report:
You have no active reminders in this chat.
output of step 6: formatted reminder list or empty message.
7a. If user provides a job name, list reminders internally first to confirm the job exists. if user provides a job ID, skip this.
7b. Ask for confirmation:
Cancel "NVDA Check - Monday 9AM"? This will stop all future runs.
7c. If user confirms, call the cancel command (by name or ID):
uv run /root/.openclaw/workspace/skills/remind-me/src/main.py cancel name "<job_name>"
or
uv run /root/.openclaw/workspace/skills/remind-me/src/main.py cancel id "<job_id>"
7d. On success, report:
✅ Reminder "NVDA Check - Monday 9AM" has been cancelled.
7e. On error or no match, report:
❌ Could not find a reminder named "NVDA Check". Want me to list your reminders?
output of step 7: success confirmation, error, or list fallback.
if no schedule provided AND cannot be reasonably assumed
if schedule is ambiguous (e.g. "often", "regularly")
if user says both "every X" AND "just once"
if tier-1 and tier-2 timezone resolution both fail
tz:UTC and state it in the confirmation.if user wants to list reminders
if user wants to cancel a reminder
if the script call fails (network, auth, gateway down)
<error>"if a reminder job is created in chat A, and user later lists from chat B
if an at: (one-shot) reminder is requested in the distant future (e.g. "in 30 days")
at:<duration> (e.g. at:720h for 30 days).if a cron expression is provided by the user (e.g. "every Monday at 9 AM")
cron:0 9 * * 1).tz: flag to the script.on successful create
✅ Done! I've set up your reminder: <job_name> , <schedule> 📱 Delivered to this chat✅ Done! I've set up your reminder: ⏰ **NVDA Check** , Every Monday at 9 AM 📱 Delivered to this chaton successful list (reminders exist)
1. **NVDA Check - Monday 9AM** 🔁 Every Monday at 9 AM ...on successful list (empty)
You have no active reminders in this chat.on successful cancel
✅ Reminder **"<job_name>"** has been cancelled.on error (script call fails)
❌ Something went wrong: <error_message>❌ Something went wrong: gateway not running. Want me to try again?on clarification needed
"How often? Every hour, every day, or something else?"on confirmation request
data format
file location
/root/.openclaw/workspace/USER.md (Timezone: field).[remind-me:channel:chatId] tag in job description (automatic).user knows it worked when:
on create: they receive a confirmation message with the job name, schedule, timezone, and "Delivered to this chat". no errors reported.
on list: they see a readable list of all active reminders in the current chat (or "no reminders" if empty). the list includes the schedule, timezone, and next-run time. no raw JSON.
on cancel: they receive a confirmation that the named reminder has been cancelled. no future firings of that job occur.