agentrem 1.2.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,34 @@
1
+ import type { Reminder } from './types.js';
2
+ export declare const DEDUP_COOLDOWN_MS: number;
3
+ export interface WatchOptions {
4
+ /** Poll interval in seconds (default 30) */
5
+ interval?: number;
6
+ /** Agent name to check for (default 'main') */
7
+ agent?: string;
8
+ /** Run check once and exit (no loop) */
9
+ once?: boolean;
10
+ /** Verbose logging */
11
+ verbose?: boolean;
12
+ /** Optional db path (for testing) */
13
+ dbPath?: string;
14
+ /**
15
+ * Override the notification function (defaults to fireNotification).
16
+ * Useful for testing — pass a no-op or spy to avoid spawning OS notifiers.
17
+ */
18
+ onNotify?: (rem: Reminder) => void;
19
+ }
20
+ export interface WatchState {
21
+ /** Map of reminder ID → timestamp of last notification */
22
+ lastNotified: Map<string, number>;
23
+ }
24
+ /** Returns true if the reminder should be notified (not in cooldown). */
25
+ export declare function shouldNotify(state: WatchState, reminderId: string, now?: number): boolean;
26
+ /** Mark a reminder as notified (records current timestamp). */
27
+ export declare function markNotified(state: WatchState, reminderId: string, now?: number): void;
28
+ /** Send a native OS notification for a single reminder. */
29
+ export declare function fireNotification(rem: Reminder): void;
30
+ /** Run a single check cycle: poll DB, notify due reminders, return notified list. */
31
+ export declare function runCheckCycle(state: WatchState, opts: WatchOptions, now?: number): Reminder[];
32
+ /** Start the watch loop. Resolves when the loop stops (only if `once` or `signal` fires). */
33
+ export declare function startWatch(opts: WatchOptions, signal?: AbortSignal): Promise<void>;
34
+ //# sourceMappingURL=watch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../src/watch.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,eAAO,MAAM,iBAAiB,QAAgB,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC3B,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,sBAAsB;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,IAAI,CAAC;CACpC;AAED,MAAM,WAAW,UAAU;IACzB,0DAA0D;IAC1D,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,OAAO,CAIrG;AAED,+DAA+D;AAC/D,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,IAAI,CAElG;AAED,2DAA2D;AAC3D,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAGpD;AAED,qFAAqF;AACrF,wBAAgB,aAAa,CAC3B,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,YAAY,EAClB,GAAG,GAAE,MAAmB,GACvB,QAAQ,EAAE,CAmCZ;AAED,6FAA6F;AAC7F,wBAAsB,UAAU,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAyCxF"}
package/dist/watch.js ADDED
@@ -0,0 +1,93 @@
1
+ // ── Watch / Notification Loop ─────────────────────────────────────────────
2
+ // Polls coreCheck() on an interval and fires native OS notifications for
3
+ // due reminders, with per-reminder dedup (5-minute cooldown).
4
+ import { getDb } from './db.js';
5
+ import { coreCheck } from './core.js';
6
+ import { truncate } from './date-parser.js';
7
+ import { buildNotifyOpts, sendNotification } from './notifier.js';
8
+ export const DEDUP_COOLDOWN_MS = 5 * 60 * 1000; // 5 minutes
9
+ /** Returns true if the reminder should be notified (not in cooldown). */
10
+ export function shouldNotify(state, reminderId, now = Date.now()) {
11
+ const last = state.lastNotified.get(reminderId);
12
+ if (last === undefined)
13
+ return true;
14
+ return now - last >= DEDUP_COOLDOWN_MS;
15
+ }
16
+ /** Mark a reminder as notified (records current timestamp). */
17
+ export function markNotified(state, reminderId, now = Date.now()) {
18
+ state.lastNotified.set(reminderId, now);
19
+ }
20
+ /** Send a native OS notification for a single reminder. */
21
+ export function fireNotification(rem) {
22
+ const opts = buildNotifyOpts(rem);
23
+ sendNotification(opts);
24
+ }
25
+ /** Run a single check cycle: poll DB, notify due reminders, return notified list. */
26
+ export function runCheckCycle(state, opts, now = Date.now()) {
27
+ const db = getDb(opts.dbPath);
28
+ let notified = [];
29
+ try {
30
+ const result = coreCheck(db, {
31
+ type: 'time,heartbeat,session,condition',
32
+ agent: opts.agent || 'main',
33
+ escalate: true,
34
+ });
35
+ const notify = opts.onNotify ?? fireNotification;
36
+ for (const rem of result.included) {
37
+ if (shouldNotify(state, rem.id, now)) {
38
+ notify(rem);
39
+ markNotified(state, rem.id, now);
40
+ notified.push(rem);
41
+ if (opts.verbose) {
42
+ console.log(`[agentrem watch] 🔔 [${rem.id.slice(0, 8)}] ${truncate(rem.content, 60)}`);
43
+ }
44
+ }
45
+ else if (opts.verbose) {
46
+ console.log(`[agentrem watch] ⏭️ [${rem.id.slice(0, 8)}] in cooldown, skipping`);
47
+ }
48
+ }
49
+ if (opts.verbose) {
50
+ console.log(`[agentrem watch] checked at ${new Date(now).toISOString()} — ` +
51
+ `${result.totalTriggered} triggered, ${notified.length} notified`);
52
+ }
53
+ }
54
+ finally {
55
+ db.close();
56
+ }
57
+ return notified;
58
+ }
59
+ /** Start the watch loop. Resolves when the loop stops (only if `once` or `signal` fires). */
60
+ export async function startWatch(opts, signal) {
61
+ const intervalMs = (opts.interval ?? 30) * 1000;
62
+ const state = { lastNotified: new Map() };
63
+ if (opts.verbose) {
64
+ console.log(`[agentrem watch] started — interval=${opts.interval ?? 30}s agent=${opts.agent ?? 'main'}`);
65
+ }
66
+ // Run immediately on start
67
+ runCheckCycle(state, opts);
68
+ if (opts.once)
69
+ return;
70
+ await new Promise((resolve) => {
71
+ let timer;
72
+ const stop = () => {
73
+ clearInterval(timer);
74
+ resolve();
75
+ };
76
+ timer = setInterval(() => {
77
+ if (signal?.aborted) {
78
+ stop();
79
+ return;
80
+ }
81
+ runCheckCycle(state, opts);
82
+ }, intervalMs);
83
+ if (signal) {
84
+ signal.addEventListener('abort', stop, { once: true });
85
+ }
86
+ // SIGINT / SIGTERM for clean shutdown when running as a daemon
87
+ process.once('SIGINT', stop);
88
+ process.once('SIGTERM', stop);
89
+ });
90
+ if (opts.verbose)
91
+ console.log('[agentrem watch] stopped.');
92
+ }
93
+ //# sourceMappingURL=watch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watch.js","sourceRoot":"","sources":["../src/watch.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentrem",
3
- "version": "1.2.2",
3
+ "version": "1.3.0",
4
4
  "description": "Structured reminders CLI for AI agents with MCP server",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -13,10 +13,12 @@
13
13
  "llms.txt",
14
14
  "llms-full.txt",
15
15
  "README.md",
16
- "LICENSE"
16
+ "LICENSE",
17
+ "assets"
17
18
  ],
18
19
  "scripts": {
19
20
  "build": "tsc",
21
+ "build:notify": "bash scripts/build-notify.sh",
20
22
  "dev": "tsc --watch",
21
23
  "test": "vitest run",
22
24
  "test:watch": "vitest",