Skip to content

Weekend-Labs/remi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

70 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Remi app icon β€” a pixel-art buddy holding a glass of water

Remi πŸ’§πŸ””

A little pixel dude who walks onto your screen and hands you a glass of water β€” and a delightful face for any notification you throw at him.

Because a notification is easy to ignore β€” a character strolling across your desktop is not.

License: MIT Platform: macOS Built with Electron Latest release

Remi walks onto the screen, offers a glass of water with a 'Time for water 3/8 today' bubble, and shows Had it / Snooze buttons

Why this exists

I miss recurring self-care reminders when I'm heads-down at work. A plain macOS notification slides in, I glance past it, it's gone. A notification is easy to ignore. A character walking across your screen is not.

So Remi is a menu-bar app that sends a small pixel-art buddy walking in from the corner holding a glass of water. He waits, you tap Had it πŸ’§ (he cheers) or Snooze 15m (he sulks off), and your progress ticks up. That's v0.1 β€” make the nudge delightful instead of nagging, and you'll actually drink the water.

v0.2 grows him up: the same buddy is now a face for any notification β€” a meeting, a Slack DM that needs a reply, an email worth your attention β€” driven by a tiny local API that cron jobs and AI agents can call. Remi stays dumb and delightful; the smarts live outside. See Reminders vs Notifications for the mental model.

Features

The water buddy

  • πŸ’§ Water reminders on a timer, only inside your configured work hours.
  • 🚢 A buddy who walks in β€” 4-frame walk cycle into the corner, arrives holding a glass, breathes with an idle bob, walks back out. Soft two-note chime on entrance.
  • βœ… Snooze / Had-it β€” two buttons (or a 30s no-answer soft snooze).
  • πŸ“… Daily history, πŸ”₯ streaks (shown in the menu-bar title + a one-off "πŸ”₯ N-day streak!" bubble), and a πŸ—“οΈ calendar view (tray β†’ View progress, a month grid tinted by how close you got each day).
  • βš™οΈ Live Settings submenu β€” interval / work-hours / goal / snooze, applied instantly.

The notification framework (new in v0.2)

  • πŸ”” Local notification API β€” a loopback HTTP server (127.0.0.1, token-auth) any producer can call. Two kinds: info peeks (buddy leans in from the edge, says one thing, retracts) and action notifications (message + buttons whose result is returned to the caller). See Notifications.
  • 🎭 Expressive poses β€” Remi reacts per state: πŸ˜” sad when you snooze water, πŸŽ‰ fists-up when you drink it, πŸ‘‹ hand-up when peeking in, 🧍 hands-folded for an action ask.
  • πŸ› οΈ remi-notify CLI + client sample + a local test UI to drive the API by hand.
  • πŸͺΆ Stays out of your way β€” frameless, transparent, always-on-top corner window, hidden ~99% of the time, never steals focus, lives in the menu bar with no dock icon.

Quickstart

git clone https://github.com/Weekend-Labs/remi.git
cd remi
npm install          # pulls Electron (dev dependency)

npm start            # run the app β€” buddy appears on the reminder timer
npm run demo         # same, but the buddy walks in ~2.5s after launch (no waiting)
npm test             # pure-logic + API unit/e2e tests (node --test, 59 tests)
npm run package      # build Remi.app into dist/ (macOS arm64, with the .icns icon)

# poke the notification API (with Remi running):
node examples/test-ui.js       # a clickable test page at http://localhost:7788
node examples/notify-client.js # or drive it from the command line

Or just download the latest release and drag Remi.app to /Applications (unsigned β€” first launch: right-click β†’ Open, or xattr -cr /Applications/Remi.app).

npm start puts Remi in your menu bar (no dock icon). Click the πŸ’§ 0/8 title for the tray menu β€” Remind now, View progress, Pause, Settings, Test peek, Quit.

πŸ”” Notifications β€” Remi as a local API

Remi runs a loopback-only HTTP server (127.0.0.1, bearer-token, never exposed to the network). Any producer β€” a cron job, a shell script, or an AI agent β€” fires a notification and (for actions) hears your reply back:

POST http://127.0.0.1:7777/notify      Authorization: Bearer <token>
{ "type": "action", "message": "Reply to Sam's DM?",
  "actions": [ { "label": "Draft reply", "result": "draft" }, { "label": "Snooze", "result": "snooze" } ] }
β†’ { "id": "n_123" }

GET  http://127.0.0.1:7777/notify/n_123 β†’ { "reply": "draft" }   # the producer hears back and acts
  • info (peek) β€” buddy leans in, delivers, strolls off. Fire-and-forget. "Standup in 5."
  • action β€” a message + buttons; your choice is returned to the producer, so an agent can then send the reply / archive the mail / open the meeting.

The token is generated on first run into config.json. Grab it, then drive the API from:

Full contract: F001 spec Β· mental model: docs/CONCEPTS.md.

The one rule: Remi stays dumb β€” no Gmail/Slack SDKs in the app. Every "is this worth interrupting me?" decision is the producer's (the model's) job. Remi is just the megaphone.

Make it your buddy

The character is me, rendered as Stardew-style pixel art from a few selfies. You can replace it with anyone β€” a photo of you, your cat, a robot β€” with the same repeatable pipeline (AI image β†’ magenta-keyed sprite β†’ cutout), including per-mood poses. Copy-paste prompts and all in:

πŸ”­ Roadmap

Shipped in v0.2 β€” the notification framework: loopback API, info peeks + action replies, remi-notify CLI, client sample + test UI, expressive per-state poses, and agent producers β€” an MCP server and a Claude skill so an AI agent can fire notifications and read your reply.

Next β€” the framework's whole point is that a new reminder type is just another producer:

What Powered by
Smart triage An agent reads Slack & email, filters the noise, only interrupts you for what matters Claude / Codex
Presence-aware delivery Buddy at the desk; WhatsApp / push when you're away or locked presence + bridge

Reminder types on deck: πŸ“… meetings Β· πŸ’¬ pending Slack DMs Β· πŸ“§ reply-worthy email Β· ⏰ ad-hoc.

πŸ’‘ A design direction, not a promise of dates. Ideas and PRs welcome.

More docs

  • docs/CONCEPTS.md β€” Reminders vs Notifications β€” the core mental model (start here).
  • docs/DESIGN-YOUR-OWN-BUDDY.md β€” build your own character (AI prompts + the tools/*.py pipeline).
  • docs/specs/ β€” feature specs (F001 notification framework, F002 Buddy V2).
  • SPEC.md β€” the product story: goal, scope, stack decisions.
  • BACKLOG.md β€” the code map and how features were scoped.

Producers (each lives with its code): mcp/ (MCP server) Β· skill/ (Claude skill) Β· examples/ (CLI client + test UI).

🍎 Platform note

macOS on Apple Silicon (arm64) only, for now. The npm run package script builds a darwin/arm64 bundle (Remi.app) with a macOS .icns icon. Nothing in the code is deeply macOS-specific β€” it's Electron β€” but the transparent, click-through, always-on-top corner overlay is only exercised on macOS, and the packaging target is hard-coded to Apple Silicon. Other targets would need a tweak to the package script and a pass over the overlay window flags. PRs welcome.


Under the hood β€” a 30-second architecture tour

Remi is deliberately tiny: a timer, a loopback API, two JSON files, and some pixel sprites. No database, no framework beyond Electron, no animation library.

β”Œβ”€ main process (src/main.js) ──────────────────────────────┐
β”‚  setInterval every 30s β†’ rollover + shouldRemind?          β”‚
β”‚  Loopback notification API (src/api.js, src/notify.js)     β”‚
β”‚  Tray menu (title + Settings submenu + calendar/pause)     β”‚
β”‚  Owns two BrowserWindows:                                  β”‚
β”‚    β€’ overlay  β†’ transparent corner window (the buddy)      β”‚
β”‚    β€’ calendar β†’ framed month-grid window (View progress)   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚ IPC (via src/preload.js)       β”‚ IPC
           β–Ό                                β–Ό
  β”Œβ”€ overlay renderer ──────────┐   β”Œβ”€ calendar renderer ────────┐
  β”‚ renderer/index.html + CSS   β”‚   β”‚ renderer/calendar.html     β”‚
  β”‚ renderer/overlay.js         β”‚   β”‚ renderer/calendar.js       β”‚
  │   water: walk-in→react→out  │   │   month grid tinted by     │
  β”‚   peek / action / poses     β”‚   β”‚   history[date]            β”‚
  β”‚ sprites: walk.png +         β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  β”‚   buddy-{hold,sad,peek,     β”‚
  β”‚   action,cheer}.png         β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β–²
   producers  β”‚ POST /notify  (cron Β· scripts Β· agents)
   β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ over 127.0.0.1 loopback + token

  Pure, tested logic (no Electron, no I/O):
    src/reminder.js      rollover Β· shouldRemind Β· applyAction Β· streak Β· todayStr
    src/calendar-grid.js monthGrid Β· dayLevel
    src/notify.js        request validation Β· queue + lifecycle Β· ttl
    src/state.js         load/save state.json + config.json Β· isValidConfig
    test/*.test.js       node --test  (reminder + settings + calendar + notify + API e2e)

The split that matters: all the interesting logic is pure functions in reminder.js, calendar-grid.js, notify.js, and state.js, tested with plain node --test and zero Electron in the loop. main.js is just wiring β€” timer, tray, windows, API, IPC. The renderer is HTML/CSS sprite animation. That's why the suite runs in milliseconds and stays green regardless of the UI.

Where your data lives

State and config are plain JSON under Electron's per-app userData directory (app.getPath('userData')). On macOS:

~/Library/Application Support/Remi/state.json    # date, glassesHad, goal, snooze, paused, history{}
~/Library/Application Support/Remi/config.json   # intervalMinutes, workHours, snoozeMinutes, goal, apiToken, apiPort

Both are created with sane defaults on first run and migrate forward if a field is missing β€” you can hand-edit them, but the Settings submenu covers everything. apiToken is the bearer token for the notification API. Delete the files to reset.

License

MIT Β© 2026 Vijay Chhuttani. Free for any purpose β€” fork it, reskin it, ship it.

About

Your reminder buddy that lives on your desktop

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors