reminder-engine

Server-side cron scheduler that polls a reminders table and delivers appointment reminders via WebSocket to the browser, which then shows Web Notifications API alerts. Use when building or debugging reminder delivery in appointment or scheduling applications.

7 stars

Best use case

reminder-engine is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Server-side cron scheduler that polls a reminders table and delivers appointment reminders via WebSocket to the browser, which then shows Web Notifications API alerts. Use when building or debugging reminder delivery in appointment or scheduling applications.

Teams using reminder-engine should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/reminder-engine/SKILL.md --create-dirs "https://raw.githubusercontent.com/heldernoid/agentic-build-templates/main/projects/healthcare-wellness/appointment-manager/skills/reminder-engine/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/reminder-engine/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How reminder-engine Compares

Feature / Agentreminder-engineStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Server-side cron scheduler that polls a reminders table and delivers appointment reminders via WebSocket to the browser, which then shows Web Notifications API alerts. Use when building or debugging reminder delivery in appointment or scheduling applications.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# reminder-engine

SQLite-backed reminder scheduler with WebSocket delivery and Web Notifications API display.

## Architecture

1. On appointment creation: insert reminder rows for each configured lead time (default: 1440 and 60 minutes before scheduled_at)
2. Server: node-cron polls the reminders table every minute for due unsent reminders, broadcasts via WebSocket, marks sent
3. Client: WebSocket listener receives event, requests notification permission if needed, shows browser notification

## Server setup

```typescript
// src/server/lib/scheduler.ts
import cron from 'node-cron';
import { WebSocketServer, WebSocket } from 'ws';
import type { Database } from 'better-sqlite3';

interface ReminderRow {
  id: number;
  appointment_id: number;
  title: string;
  scheduled_at: string;
}

export function startScheduler(db: Database, wss: WebSocketServer): void {
  cron.schedule('* * * * *', () => {
    const now = new Date().toISOString();
    const due = db.prepare(
      `SELECT r.id, r.appointment_id, a.title, a.scheduled_at
       FROM reminders r
       JOIN appointments a ON a.id = r.appointment_id
       WHERE r.remind_at <= ? AND r.sent = 0 AND a.status = 'upcoming'`
    ).all(now) as ReminderRow[];

    const markSent = db.prepare('UPDATE reminders SET sent = 1 WHERE id = ?');

    for (const row of due) {
      broadcast(wss, {
        type: 'appointment_reminder',
        appointmentId: row.appointment_id,
        title: row.title,
        scheduledAt: row.scheduled_at,
      });
      markSent.run(row.id);
    }
  });
}

function broadcast(wss: WebSocketServer, data: object): void {
  const message = JSON.stringify(data);
  wss.clients.forEach((client) => {
    if (client.readyState === WebSocket.OPEN) {
      client.send(message);
    }
  });
}
```

## Inserting reminder rows on appointment creation

```typescript
// Call after INSERT INTO appointments
function scheduleReminders(
  db: Database,
  appointmentId: number,
  scheduledAt: string,
  leadMinsArray: number[]
): void {
  const insert = db.prepare(
    'INSERT INTO reminders (appointment_id, remind_at, lead_mins) VALUES (?, ?, ?)'
  );
  for (const leadMins of leadMinsArray) {
    const remindAt = new Date(
      new Date(scheduledAt).getTime() - leadMins * 60_000
    ).toISOString();
    insert.run(appointmentId, remindAt, leadMins);
  }
}

// Usage: scheduleReminders(db, newId, scheduledAt, [1440, 60]);
```

## Client setup

```typescript
// src/client/src/lib/notifications.ts

export async function requestPermission(): Promise<boolean> {
  if (!('Notification' in window)) return false;
  if (Notification.permission === 'granted') return true;
  if (Notification.permission === 'denied') return false;
  const result = await Notification.requestPermission();
  return result === 'granted';
}

interface ReminderEvent {
  type: string;
  appointmentId: number;
  title: string;
  scheduledAt: string;
}

export function connectReminders(
  onReminder: (event: ReminderEvent) => void
): WebSocket {
  const ws = new WebSocket(`ws://${window.location.host}/ws`);
  ws.onmessage = (event) => {
    const data = JSON.parse(event.data as string) as ReminderEvent;
    if (data.type === 'appointment_reminder') {
      onReminder(data);
    }
  };
  return ws;
}

export function showAppointmentReminder(title: string, scheduledAt: string): void {
  if (Notification.permission !== 'granted') return;
  const date = new Date(scheduledAt).toLocaleString();
  const n = new Notification('Appointment reminder', {
    body: `${title} - ${date}`,
    icon: '/icon.png',
    tag: `appt-${title}-${scheduledAt}`,
    requireInteraction: false,
  });
  n.onclick = () => { window.focus(); n.close(); };
}
```

## React integration

```typescript
import { useEffect } from 'react';
import { requestPermission, connectReminders, showAppointmentReminder } from './lib/notifications';

export function useReminders(): void {
  useEffect(() => {
    requestPermission();
    const ws = connectReminders(({ title, scheduledAt }) => {
      showAppointmentReminder(title, scheduledAt);
    });
    return () => ws.close();
  }, []);
}
```

## Cancelling reminders when appointment is updated

When an appointment is cancelled, deleted, or rescheduled, delete or mark the existing reminder rows as sent to prevent stale notifications.

```typescript
function cancelReminders(db: Database, appointmentId: number): void {
  db.prepare('UPDATE reminders SET sent = 1 WHERE appointment_id = ?').run(appointmentId);
}

function rescheduleReminders(
  db: Database,
  appointmentId: number,
  newScheduledAt: string,
  leadMinsArray: number[]
): void {
  db.prepare('DELETE FROM reminders WHERE appointment_id = ? AND sent = 0').run(appointmentId);
  scheduleReminders(db, appointmentId, newScheduledAt, leadMinsArray);
}
```

## Troubleshooting

### Reminders not firing

1. Confirm node-cron is running: check server logs for cron tick messages
2. Verify `remind_at` values are in ISO 8601 UTC format in the database
3. Check that the appointment status is still `'upcoming'` (cancelled appointments are excluded)

### WebSocket disconnects

1. Add reconnect logic on `ws.onclose`: use exponential backoff before reconnecting
2. Verify the WebSocket server path `/ws` matches the Express server configuration

### Browser notifications not showing

1. `Notification.permission` must be `'granted'` - call `requestPermission()` on user interaction
2. On macOS: System Settings - Notifications - check the browser is allowed
3. Some browsers block notifications on HTTP (non-localhost) origins

Related Skills

Skill: queue-engine

7
from heldernoid/agentic-build-templates

## When to use this skill

sql-tutorial-engine

7
from heldernoid/agentic-build-templates

No description provided.

flashcard-engine

7
from heldernoid/agentic-build-templates

Spaced repetition flashcard system with SM-2 scheduling, Markdown support, and Anki import. Use when you need to study or manage flashcard decks, grade cards, check review schedules, or import/export card data. Triggers include "flashcards", "spaced repetition", "study cards", "Anki import", "SM-2", "review schedule".

docker-engine

7
from heldernoid/agentic-build-templates

Interact with the Docker Engine API via dockerode in Node.js/TypeScript -- listing containers, streaming logs, collecting stats, and running Compose operations as subprocesses.

prompt-engineering

7
from heldernoid/agentic-build-templates

No description provided.

Skill: Uptime Monitoring

7
from heldernoid/agentic-build-templates

## Overview

Skill: Status Page

7
from heldernoid/agentic-build-templates

## Overview

Skill: unit-conversion

7
from heldernoid/agentic-build-templates

## Overview

Skill: recipe-scaler

7
from heldernoid/agentic-build-templates

## Overview

reading-list

7
from heldernoid/agentic-build-templates

Operate the reading-list API to save, manage, tag, search, and export articles.

email-digest

7
from heldernoid/agentic-build-templates

Configure, test, and troubleshoot the reading-list daily email digest delivered via nodemailer.

websocket-realtime

7
from heldernoid/agentic-build-templates

Use the WebSocket connection in poll-builder to receive live vote updates. Use when you need to stream real-time poll results, monitor a poll for new votes, or build a live dashboard. Triggers include "live results", "real-time updates", "stream votes", "watch poll", or "WebSocket".