DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

Smallballoons01 /

Smallballoons01/dsh-water-reminder

Verified

Hydration reminder plugin for DeepSeek Harness — a randomized 40-60 minute nudge delivered as a native OS notification, an in-conversation agent nudge, and a sidebar countdown panel. Zero runtime dependencies.

★ 1 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@338b003a

dsh-water-reminder

A hydration reminder plugin for DeepSeek Harness.

People forget to drink while they are deep in work. While a session is live this plugin reminds every 40–60 minutes, drawn at random, and delivers each reminder through three independent channels — missing one still leaves two:

Channel How it lands Needs
🖥️ OS notification osascript on macOS, a WinRT toast on Windows, notify-send on Linux ctx.subprocess (part of dsh-base)
💬 In-conversation nudge a plugin message injected into every live root session, so the assistant says one line the agents service
🔔 Browser notification the sidebar panel watches reminderSeq and the tab raises its own notification page open + user permission

Plus a sidebar countdown panel, a settings page, and an embedded hydration-coach skill.

Why 40–60 at random

The irregularity is the point. A perfectly regular timer is what teaches people to ignore it — which is why "drink water on the hour" apps get switched off. Redrawing the interval from the band on every reminder keeps the rhythm unpredictable, which is what makes it work.

Install

# from a local checkout (writes the profile's package.json and dsh.profile.bundles)
dsh plugin --profile web add link:/path/to/water_reminder

# or from a packed tarball
pnpm pack && dsh plugin --profile web add ./dsh-plugin-water-reminder-0.1.0.tgz

Then restart dsh web: the timer and the tools mount at session start. Never keep two water-reminder rows in one profile — each would start its own timer and register the /water/api prefix route, and the duplicate route makes the whole plugin tree fail to boot.

Tools

Tool Purpose
water_status Today's intake, remaining, cups, goal streak, current band, quiet hours, next reminder, 7-day series
water_log Record a cup (defaults to the configured cup size; accepts milliliters and note). Append-only, never deduplicated
water_snooze Defer the next reminder; defaults to 5 minutes
water_control start / stop / set-band / set-goal / remind-now / reset

water_control writes runtime overrides into the state file rather than editing the profile config; reset drops those overrides and falls back to configuration.

Configuration

In the profile's cordis.patch.yml (defaults ship in the plugin's own cordis.patch.yml):

Field Type Default Meaning
enabled boolean true Whether the loop runs at start-up
intervalMinMinutes number 40 Lower bound of the random interval, in minutes
intervalMaxMinutes number 60 Upper bound of the random interval, in minutes
dailyGoalMl number 2000 Daily intake goal
cupMl number 250 Milliliters recorded for one "cup"
snoozeMinutes number 5 Default deferral for water_snooze
quietHours.enabled boolean false Hold reminders inside a local-time window
quietHours.from / .to string 22:00 / 08:00 The window (may wrap past midnight); a draw inside it resumes at the window's end
notify.os boolean true OS notification
notify.agent boolean true In-conversation nudge
notify.sound boolean true Play a sound with the OS notification
stateFile string $DSH_HOME/water-reminder.json Persisted state location
promptSection boolean true Inject the reminder briefing into the system prompt
skill boolean true Register the embedded hydration-coach skill

The band is clamped to [5, 720] minutes, the goal to [200, 20000] ml, and the cup to [50, 2000] ml. A user patch replaces the whole entry config, so restate every field you want to keep when overriding one.

Web surfaces

  • Sidebar footer 💧 button — carries the live countdown to the next reminder (shows "Paused" while off, ✅ once the goal is met). The panel has today's progress bar, cups/streak/band, next reminder, a 7-day bar chart, +250ml logging, snooze, remind-now, pause/resume, and a control to grant browser-notification permission.
  • Settings page — the full rhythm/goal/quiet-hours/channels form. Writes land in $DSH_HOME/settings.yaml and hot-reload; changing the band or quiet hours rearms the timer immediately.

The two switches marked "(restart)" (promptSection, skill) are read at load time and need a Harness reload.

Skill

The same instructions ship two ways:

  1. Runtime registration — at mount the plugin calls ctx.skills.register(), so the model sees hydration-coach in its skill catalog with no filesystem skill root at all.
  2. On disk — skills/hydration-coach/SKILL.md, for deployments that prefer a filesystem skill root (~/.dsh/skills, ~/.agents/skills, a project .dsh/skills).

skill.js is the single source of truth and npm test asserts the checked-in SKILL.md matches the embedded body byte for byte, so the two delivery paths cannot drift. Run npm run skill after editing the body.

The skill covers which tool to call when, how to react to a reminder (one short line, no health lecture, never derail the current task), logging rules, how to retune the rhythm, estimating a personal goal from body weight (30 ml × kg), and tone/safety boundaries (no medical claims).

How the reminder loop behaves

  • The state file owns the state. Timer, next due time, and intake log live in $DSH_HOME/water-reminder.json, written through a temp file plus rename so a crash cannot truncate it.
  • No backlog after a restart. A stale nextDueAt draws a fresh interval instead of firing the missed reminders at once.
  • Quiet hours push a draw inside the window to the window's end rather than redrawing, so the behaviour stays predictable.
  • A manual reminder leaves the rhythm alone. remind-now delivers once without resetting the pending reminder.
  • Disposal drains. Unloading the plugin waits for an already-queued state write, so a cup logged at the last moment is not lost.
  • The timer is unref()ed, so the reminder loop never keeps the process alive.

Known limitations

  • Reminders only exist while Harness is alive. There is no email, SMS, or push channel. With the process down, only the OS notification path exists — and only while it is up.
  • The Windows toast needs WinRT, invoked as powershell -EncodedCommand (UTF-16LE base64) so quoting and CJK text are safe. If PowerShell is restricted by policy the reminder degrades to a log line, while the other two channels still fire.
  • Browser notifications need a user gesture to request permission; the 🔔 control in the panel is that gesture.
  • The 7-day series is panel-only. water_status returns a compact one-line text form instead, which keeps the tool's output schema free of nested arrays.

Development

Prerequisites

The plugin has zero runtime dependencies — @deepseek-ai/dsh-tools, dsh-llm, schemastery and friends are peer dependencies that the Harness supplies at runtime.

Note, though, that the versions this plugin is developed against are not published to npm (npm's latest @deepseek-ai/dsh-tools is 0.0.1-rc.1; this plugin targets 0.1.6-alpha.1), so a fresh clone cannot npm install a working test environment. Pick one of three:

# 1) Run the dependency-free part only — works immediately after cloning
npm test              # 35 pass / 2 skip, and each skip says why

# 2) Reuse the packages from an existing local Harness profile (fastest, offline)
mkdir -p node_modules/@deepseek-ai
for p in cordis dsh-llm dsh-tools dsh-settings dsh-skill dsh-system-prompt schemastery; do
  ln -sfn "$HOME/.dsh/profiles/node_modules/@deepseek-ai/$p" "node_modules/@deepseek-ai/$p"
done
npm test              # 60 pass, including the real cordis composition tests

# 3) Build the Harness from source and link this plugin the way it documents
git clone https://github.com/deepseek-ai/deepseek-harness

Use 2 or 3 for the full suite; 1 is enough when you are only touching pure logic (hydration.js / notify.js).

Scripts

npm test              # 60 tests with dependencies; 35 pass + 2 skip without
npm run skill         # regenerate SKILL.md from skill.js
npm run skill:check   # verify SKILL.md is in sync
File Coverage Needs Harness
test/hydration.test.js Pure logic: band normalization, random-draw bounds, quiet hours across midnight, streaks, summary, config completion no
test/notify.test.js Exact per-platform argv, AppleScript/XML escaping, PowerShell encode round-trip no
test/skill.test.js Embedded skill identical to the on-disk SKILL.md byte for byte, spec-compliant name, tool names complete no
test/plugin.test.js Tool behavior against a minimal fake context, asserting every result matches its own declared output.schema yes
test/integration.test.js Mounted on a real Context with the real tools/skills/systemPrompt services: dependency resolution, skill discoverability, clean disposal, and no disk writes during teardown yes

When the packages are missing, the last two files skip with a printed reason instead of turning the suite red.

License

MIT — see LICENSE.

—/ 5

No ratings yet

Verified DSH bundle

Commit 338b003a4542

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout