3.4 KiB
3.4 KiB
/remind
Create, list, edit, enable, disable, and remove recurring or one-time reminders.
How it works
- Storage: SQLite (
db/reminders.sqlite) — atomic transactions, no YAML races. - Schema:
reminders(text, enabled, timezone, timestamps, soft-delete) +schedule_at/schedule_cron/schedule_random+reminder_fires(dedup + audit). - Sender:
remind_send.pyruns every minute from the user crontab. Reads SQLite, finds due fires, sends to Telegram, logs delivery. - Deduplication: Every delivery is recorded in
reminder_fireswith statusdelivered/failed. One-timeatreminders fire exactly once; cron and random fire once per computed slot. - Audit: All mutations and deliveries are logged to
log/reminder.log.
Commands
Create a reminder
/remind drink water every day at 9:00
/remind stand up every weekday at 9:30
/remind buy milk at 2026-06-15T18:00
/remind stretch randomly 2 times between 08:00 and 20:00
/remind občanka pana Přibyla once daily at random time between 8:00 and 21:00
The LLM parses natural language and calls remind_edit.py add with the appropriate flags:
--text "..."--cron "0 9 * * *"(repeatable)--at "2026-06-15T18:00:00"(repeatable)--random-times-per-day N --random-window HH:MM-HH:MM [--random-days DOW] [--random-from YYYY-MM-DD] [--random-until YYYY-MM-DD]
List reminders
/remind list
Calls remind_edit.py list → JSON with all active reminders and their schedules.
Edit a reminder
/remind edit keyword --text "new text"
/remind edit keyword --replace-schedules --cron "0 10 * * *"
Calls remind_edit.py edit --keyword <keyword>. Keyword is matched case-insensitively against reminder text. Ambiguous matches are rejected.
Enable / Disable
/remind disable keyword
/remind enable keyword
Remove a reminder
/remind remove keyword
Soft delete (sets deleted_at). Hard delete happens only via direct DB access.
Files
| File | Purpose |
|---|---|
skills/remind/scripts/db.py |
Schema, connection factory (get_db), init_db(), audit log_operation() |
skills/remind/scripts/remind_edit.py |
CRUD CLI: list, add, remove, edit, enable, disable |
skills/remind/scripts/remind_send.py |
Sender: reads SQLite, finds due fires, sends Telegram, dedups |
skills/remind/scripts/random_times.py |
Deterministic random time generator (seeded by text + date) |
scripts/migrate_yaml_to_sqlite.py |
One-shot migration from old reminder.yaml to SQLite |
skills/remind/tests/ |
pytest suite: test_db.py, test_remind_edit.py, test_remind_send.py, test_random_times.py |
Crontab
* * * * * uv run /home/nanobot/.nanobot/workspace/skills/remind/scripts/remind_send.py >> /home/nanobot/.nanobot/workspace/log/reminder_cron.log 2>&1
Environment
REMIND_DB— override SQLite path (used in tests).python3is required;pythonis not available in this runtime.
Design decisions
- SQLite WAL mode — readers don't block writers.
- Soft delete — preserves history and foreign-key integrity.
- Deterministic random — same text + date always yields same times, so dedup works across restarts.
- JSON output — both edit and send scripts emit structured JSON for easy LLM parsing.
- No YAML — eliminated race conditions, manual string construction, and fragile parsing.