@dingyi222666/dsh-wakatime
September 4, 2026 · View on GitHub
English | 中文
WakaTime plugin for DeepSeek Harness (dsh) — track your AI coding activity, lines of code, and time spent. Adapted from opencode-wakatime to dsh's plugin model.
Install
# Install from npm (requires dsh >= 0.1.3-alpha.1)
dsh plugin --profile web add @dingyi222666/dsh-wakatime
# Restart dsh web for it to take effect
dsh web
The plugin works in any profile that runs the agent loop — web, headless, tui, … — install it into each profile you use:
dsh plugin --profile headless add @dingyi222666/dsh-wakatime
From source (GitHub)
git clone https://github.com/dingyi222666/dsh-wakatime
cd dsh-wakatime
pnpm install && pnpm run build
dsh plugin --profile web add .
dsh web
Notes:
dsh pluginbehaves like adding a dependency to your profile. A bundle plugin is loaded once its full package name appears in the profile'sdsh.profile.bundleslist (added automatically); the bundle patch (cordis.patch.yml) applies on the next boot.- To update, run the same command again.
- With the repo source-launched CLI, run the args through the bin directly (
node --import tsx/esm apps/cli/src/bin.ts plugin --profile web add @dingyi222666/dsh-wakatime).
Configuration
The plugin works out of the box. To override behavior, add a row with the
same id (wakatime) in your profile's user patch layer
($DSH_HOME/profiles/<name>/cordis.patch.yml) or via --patch:
- id: wakatime
config:
heartbeatIntervalMs: 120000 # rate limit per project (default 60000)
debug: true # force DEBUG logging (default: ~/.wakatime.cfg debug=true)
client: web # client qualifier in the --plugin string (default "dsh")
timeoutMs: 45000 # heartbeat CLI timeout (default 30000)
All fields are optional and validated by a schemastery schema at load.
Features
- Automatic CLI management — downloads and updates
wakatime-cliautomatically, or uses a global install (brew install wakatime-cli) - Detailed file tracking — tracks file operations the agent performs:
edit,write,read,read_image, andstr_replace_editor(view/create/str_replace/insert) - Resolved-path accuracy (dsh 0.1.3-alpha.1) — reads the fs tools' durable
tool/resultmeta: resolved (sandbox-aware) entity paths and exact diff hunks win over raw call arguments when present - AI coding metrics — sends
--ai-line-changesfor WakaTime's AI coding analytics, computed exactly from the fs tools' diff hunks (context lines excluded) - Live activity heartbeats (dsh 0.1.3-alpha.1) —
agent/statustransitions and theagent/assistant-streamfirehose heartbeat the current file in near real time while a long turn streams, instead of waiting for the durable settlement - Rate-limited heartbeats — 1 per minute per project, persisted to disk so parallel dsh processes share the budget (durable changes and live activity draw from the same budget)
- Session lifecycle — force-flushes pending heartbeats when a session is disposed and when the plugin tree tears down, so one-shot
dsh --profile headlessruns still report their activity - Batch tool support — multiple files in one edit are sent in a single
wakatime-cliinvocation via--extra-heartbeats - Zero runtime dependencies — the built plugin imports only Node builtins plus the
@deepseek-ai/*peers the host already provides
Prerequisites
WakaTime API Key
Ensure you have a WakaTime API key configured in ~/.wakatime.cfg
(or $WAKATIME_HOME/.wakatime.cfg when WAKATIME_HOME is set):
[settings]
api_key = waka_your_api_key_here
Get your API key from WakaTime Settings.
WakaTime CLI (Optional)
The plugin downloads wakatime-cli automatically when missing. To install it yourself:
brew install wakatime-cli
or download from WakaTime releases.
How It Works
The plugin subscribes to dsh's session event firehose (session/event):
flowchart TB
subgraph dsh["DeepSeek Harness"]
A[Agent Loop] --> B[tool/call + tool/result events<br/>(meta: resolved path + diff hunks)]
C[Live agent events<br/>agent/status, agent/assistant-stream] --> D[Activity]
C2[Chat Activity<br/>user/message, assistant/message, assistant/attempt] --> D
B --> D
D --> E[Session Events<br/>turn/end, session/disposed]
end
subgraph Plugin["dsh-wakatime"]
B --> F[Join callId: extract file<br/>path + line changes from<br/>meta and fs diff hunks]
F --> G[Per-project pending<br/>changes + last entity]
C --> H[Rate-limited<br/>heartbeat batch]
G --> H
E --> I[Final force flush]
H --> J[wakatime-cli<br/>--ai-line-changes<br/>--extra-heartbeats]
I --> J
end
tool/callrecords the tool name and parsed arguments bycallId;tool/resultmatches it back and reads the fs tools' durablemetapayload — resolved entity paths (read,read_image) and diff hunks (edit,write) — for exact per-hunk line counts, falling back to the call arguments when a host attaches no meta (writecontent,str_replace_editorstrings).- Heartbeats are sent at most once per minute per project (state file under
~/.wakatime/dsh-wakatime/), on chat activity, tool results, committed model settlements (including message-lessassistant/attemptrecords in dsh 0.1.3-alpha.1), live agent activity (agent/status,agent/assistant-stream), turn boundaries, session disposal, and plugin teardown. - The
--plugintag reportsDeepseek Harness[-<client>]/<dsh version> dsh-wakatime/<version>.
Development
pnpm install
pnpm run typecheck # tsc --noEmit
pnpm run build # declarations into lib/types + tsdown bundle lib/index.js
pnpm test # vitest: changes, state, heartbeat, plugin wiring
Layout:
src/index.ts— plugin entry (name/Config/apply) and event wiringsrc/config.ts— schemasteryConfigschema, defaults,--plugintagsrc/changes.ts— tool events → file changes, diff line countingsrc/state.ts— per-project rate limitingsrc/heartbeat.ts—wakatime-cliinvocation, batching, flushingsrc/cli.ts—wakatime-clidiscovery/download/updatesrc/paths.ts,src/logger.ts— WakaTime paths and file loggingtests/— unit tests plus an integration test that drives the plugin over a real cordisContext
Known Limitations
- Tool calls executed inside sandboxed/remote filesystems are tracked by their
model-visible
file_patharguments unless the tool/resultmetacarries a resolved absolute path (dsh 0.1.3-alpha.1 fs tools do); paths the sandbox resolves differently may land as project-relative entities. bashcommands are not attributed to files (they can touch anything).- The dsh host version in the
--plugintag isunknownwhen the@deepseek-ai/dshpackage cannot be resolved from the plugin's location (e.g. an npm install without the dev dependency present). - The
@deepseek-ai/dsh-session/@deepseek-ai/dsh-agentpeer ranges start at0.1.3-alpha.1; older hosts run the tracking paths but get no live agent activity heartbeats.
License
MIT — ported logic from opencode-wakatime (MIT).