Skip to content
dsh-market Browse plugins GitHub 中文

zhengjy01/dsh-task-dispatcher

TickTick (滴答清单) daily task dispatcher: an interval timer pulls due tasks from a chosen list into a Markdown task file and notifies only when the task set changes (WeChat via ClawBot / flomo / macOS); it can also auto-execute each task in its own headless DSH session under a configurable timeout, optionally gated to DeepSeek's off-peak hours so peak-time work queues to the next off-peak window instead of starting, plus a settings panel for the deferred write-back queue and dispatcher_status / dispatcher_config / dispatcher_run / dispatcher_report tools.

Stars ★ 1 Category Notifications & Integrations Listed 2026-09-30 npm dsh-task-dispatcher

Install

Inside DeepSeek Harness, with dsh-market

dsh plugin --profile web add dshmarket

Or from the command line

dsh plugin --profile web add dsh-task-dispatcher

Installing runs third-party code with your own permissions — it can read your files, use your credentials and reach the network. Review the source first, and pin a commit (github:owner/repo#sha) when you can.

README

Use TickTick (滴答清单) as DSH's daily task dispatcher: a cordis timer pulls today's due tasks from the configured source list (default 5️⃣AI; point it at In progress to dispatch from the "doing" list), writes them to today's task file, and notifies via WeChat (ClawBot), flomo, or macOS (configurable). The agent reads the file, works through the items, and writes results back to TickTick with ticktick_complete.

Reuses the already-installed dsh-ticktick plugin (its data layer + OAuth credentials in ~/.dsh/dsh-ticktick.json) — no duplicate configuration.

Features

  • Interval dispatch: cordis ctx.interval polls every minute and dispatches when the configured interval (minutes) has elapsed; 0 disables the timer; dispatcher_run dispatches manually anytime.
  • Task source: default the 5️⃣AI list (set it to In progress to dispatch from the "doing" list; projectName / projectId configurable); dueMode=today pulls due-today/overdue + undated, dueMode=all pulls all incomplete.
  • Today task file: writes a Markdown checklist (default ~/.dsh/dsh-task-dispatcher/today-tasks.md).
  • Execution-result receipts (notifyResult, on by default): every auto-executed task pushes a SHORT outcome message (OK/FAIL + title + a one-line result, or the failure reason such as a worker timeout) as soon as its worker settles, plus a compact batch tally when a pass runs more than one task - a run reports back by itself instead of relying on the worker calling dispatcher_report. The summary is the worker's stdout final answer (progress/reasoning go to stderr), stripped of ANSI and the DONE marker and capped at 220 characters.
  • Notify on change only: notifyWechat (WeChat, relayed through the local DSH-WeChatClawBot gateway; the recipient is auto-detected) + notifyFlomo (reuses ~/.dsh/dsh-flomo.json) + notifyMac (osascript); a repeated pull with no new tasks stays silent.
  • WeChat notifications (ClawBot): the plugin POSTs the text to the local gateway /send (default http://127.0.0.1:51235), which relays it to the WeChat account that scanned the QR code. The plugin never talks to WeChat directly and stores no WeChat credentials. The recipient defaults to the userId recorded in the ClawBot state dir (~/.dsh-wechat, override with DSH_WECHAT_HOME) under accounts/<botId>.json, or can be set explicitly with wechatTo; long bodies are split into 1200-character chunks. An unavailable channel only marks that channel as failed — it never fails the dispatch.
  • Auto-execute (autoExecute): when on, each pulled task runs in its own dsh --profile headless session (serial, one task per session); success writes back to TickTick via ticktick_complete; failed tasks wait out the retry cooldown.
  • Worker timeout (workerTimeoutMinutes): each auto-execute worker is SIGKILLed after this many minutes (default 30, range 1–1440), configurable via dispatcher_config / the settings panel. It used to be a hardcoded 10 minutes, which killed long-but-healthy tasks and reported them as failures.
  • Cheap mode (cheapMode, off by default): when on, auto-execution only starts during DeepSeek's off-peak (discounted) hours; during peak it queues tasks to the next off-peak start (cheapStrategy=wait) or skips the round (skip). The gate sits strictly before a worker is spawned — pull cadence, filtering and serial execution are untouched; off means 100% legacy behaviour. Official schedule (peak/off-peak pricing since 2026-08-17): peak = Mon–Fri 09:00–12:00 and 14:00–18:00 Beijing time; everything else is off-peak (~half price). The windows are not hardcoded: cheapPreset (official-2026 / legacy-utc old UTC 16:30–00:30 / custom) + peakWindows + cheapTimezone. Queued tasks and nextCheapStartAt are persisted, so a DSH restart loses nothing; dispatcher_run honours cheap mode by default and accepts ignoreCheapMode: true to bypass it. Notifications distinguish “queued for the HH:MM off-peak window” from a normal run.
  • Worker workspace (workerWorkspaceId): the auto-execute worker session runs in the user's home dir by default; set a DSH workspace id and the worker spawns with cwd = that workspace's directory, so files are written there and the produced session shows up under that workspace in the GUI sidebar. The settings panel provides a workspace dropdown (list read from ~/.dsh/storages/workspace.json); empty = home dir.
  • Deferred-sync panel (agent -> TickTick): the settings page configures the write-back path directly - silence threshold (idleMinutes), per-session top-level cap, and the launchd check interval - and shows the queue, the whole-harness idle time, timer state and the last flush result. Buttons: save thresholds, flush now, force flush, reload timer. The control plane only reads the queue file and drives the script/timer; the writing itself stays in the standalone script + launchd, so a flush still happens while DSH is closed, and no write logic is duplicated between the two halves. Routes: GET /api/dsh-task-dispatcher/deferred, POST .../deferred/{config,flush,timer}.
  • Agent tools: dispatcher_status / dispatcher_config / dispatcher_run / dispatcher_report + a Web settings panel.

Compatibility

Requires DeepSeek Harness ≥ 0.1.5-rc.1 (declared as dsh.engines.dsh in the package manifest, so the DSH plugin marketplace can report it) and is verified against 0.1.5-rc.1. This build carries the DSH 0.1.5 adaptations: the strict tool-result contract (lossless-JSON snapshot, additionalProperties: false schema validation, and output.render returning ContentBlock[]) plus executable resolution that survives a launchd-started host whose PATH is only /usr/bin:/bin. Since this version the peerDependencies range explicitly declares compatibility with DSH 0.2.0-rc.2 (^0.2.0-rc.2 is now included), so no compatibility warning appears on 0.2.0-rc.2. No functional change.

Install

The shape is install → restart → confirm it loaded. Every step states the expected result, so you can tell success from failure.

  1. Install the plugin: run dsh plugin --profile web add dsh-task-dispatcher in a terminal. Expected: the command exits cleanly and dsh-task-dispatcher shows up in the dependencies of ~/.dsh/profiles/web/package.json.
  2. Restart DSH: restart dsh web (sidebar “restart”, or re-run the same start command). Expected: the GUI comes back with no plugin-loading errors in the startup output.

📷 【Screenshot slot 2】proves the plugin actually installed How to capture: in the terminal, stop right after step 1's command finishes (or on the DSH startup lines after the restart); crop: the terminal window Redact: local absolute paths, your username, any token To fill in: save the shot to docs/images/dsh-task-dispatcher-01-installed.png, then replace this whole block with ![After install](docs/images/dsh-task-dispatcher-01-installed.png)

  1. Confirm it loaded: ask the agent to run dispatcher_status (or check that “Task dispatcher” appears on the settings page). Expected: output starts with 插件状态:已启用 / status “enabled” and lists the source list, pull interval and notification channels.

Enable & configure

  1. Open the settings page: DSH Web → Settings → expand the “Task dispatcher” card. Expected: you see the master switch, pull interval, task source, notification channels and auto-execute blocks.

📷 【Screenshot slot 4】proves where the settings live (the step people get stuck on) How to capture: Settings → stop on the top half of the expanded “Task dispatcher” card (master switch + pull interval + source list) Redact: workspace paths / list names if they carry personal information To fill in: save the shot to docs/images/dsh-task-dispatcher-02-settings.png, then replace this whole block with ![Task dispatcher in Settings](docs/images/dsh-task-dispatcher-02-settings.png)

  1. Adjust what you need: set the task source (default 5️⃣AI; use In progress to dispatch from the “doing” list), the pull interval (0 disables the timer), the notification channels (WeChat ClawBot / flomo / macOS; WeChat is off unless you switch it on) and auto-execute (each task then runs in its own headless session). Expected: the status line refreshes after saving and dispatcher_status reports the same values.
  2. Turn on cheap mode (optional; the headline of this release): in the same card, tick “省钱模式(自动执行只在 DeepSeek 空闲时段开跑)” (off by default), then pick the preset (default “official, Mon–Fri 09–12 / 14–18 peak”), the strategy (default “wait for off-peak”, or skip) and the tail margin (set it to your worker timeout, e.g. 15 / 30). Expected: a peak-window editor appears, and the status line shows “current window / next run / queued tasks”.

📷 【Screenshot slot 6】proves cheap mode is configured correctly How to capture: Settings → “Task dispatcher” → stop on the fully expanded cheap-mode area (toggle + preset + strategy + tail margin + window table) Redact: none (just keep the workspace path at the top out of frame) To fill in: save the shot to docs/images/dsh-task-dispatcher-03-cheap-mode.png, then replace this whole block with ![Cheap mode settings](docs/images/dsh-task-dispatcher-03-cheap-mode.png)

Verify it works

  1. Trigger one dispatch: ask the agent to run dispatcher_run, or press pull now in the panel; to “run once right now even in peak”, press “省钱模式(高峰也马上执行,仅本次)”. Expected: today's task file ~/.dsh/dsh-task-dispatcher/today-tasks.md gains items (the agent reads it and works through them), and you get one notification when the task set changed.
  2. Look at the result: run dispatcher_status, or read the status line in the panel. Expected: “last pull: · items”; with cheap mode on during peak you also see “queued: N” and “next run: ” — tasks held back for the next off-peak window is exactly what cheap mode looks like.

📷 【Screenshot slot 8】proves it is actually in use (the most important one) How to capture: after one dispatch, stop on the panel status line, or on the “last pull / queued / next run” lines of dispatcher_status; crop: just that card or that terminal output Redact: real task titles, local paths, WeChat display name — use a throwaway test task To fill in: save the shot to docs/images/dsh-task-dispatcher-04-in-use.png, then replace this whole block with ![In use](docs/images/dsh-task-dispatcher-04-in-use.png)

Current release: v0.6.0 (CHANGELOG · Releases · npm).

Dev

pnpm install
pnpm build && node tests/smoke.mjs

License

MIT

Content from the project README on GitHub ↗

Comments

Comments live in GitHub Discussions. Sign in with GitHub to post or react.