Bundle
@dingyi222666/dsh-wakatime
WakaTime plugin for DeepSeek Harness (dsh) — track AI coding activity, lines of code, and time spent
- Source
- dingyi222666
- stars
- 3 stars
- License
- MIT
- Updated
- Updated 8 hours ago
Readme
# @dingyi222666/dsh-wakatime
[](https://www.npmjs.com/package/@dingyi222666/dsh-wakatime)
[](https://github.com/dingyi222666/dsh-wakatime)
English | [中文](README.zh.md)
WakaTime plugin for [DeepSeek Harness (dsh)](https://github.com/deepseek-ai/deepseek-harness) — track your AI coding activity, lines of code, and time spent. Adapted from [opencode-wakatime](https://github.com/angristan/opencode-wakatime) to dsh's plugin model.
## Install
```sh
# Install from npm (requires dsh >= 0.1.3-alpha.2)
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:
```sh
dsh plugin --profile headless add @dingyi222666/dsh-wakatime
```
### From source (GitHub)
```sh
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 plugin` behaves like adding a dependency to your profile. A bundle plugin is loaded once its full package name appears in the profile's `dsh.profile.bundles` list (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`:
```yaml
- 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-cli` automatically, or uses a global install (`brew install wakatime-cli`)
- **Detailed file tracking** — tracks file operations the agent performs: `edit`, `write`, `read`, `read_image`, and `str_replace_editor` (`view`/`create`/`str_replace`/`insert`)
- **Resolved-path accuracy (dsh 0.1.3-alpha.1)** — reads the fs tools' durable `tool/result` `meta`: resolved (sandbox-aware) entity paths and exact diff hunks win over raw call arguments when present
- **AI coding metrics** — sends `--ai-line-changes` for 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/status` transitions and the `agent/assistant-stream` firehose 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 headless` runs still report their activity
- **Batch tool support** — multiple files in one edit are sent in a single `wakatime-cli` invocation 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):
```ini
[settings]
api_key = waka_your_api_key_here
```
Get your API key from [WakaTime Settings](https://wakatime.com/api-key).
### WakaTime CLI (Optional)
The plugin downloads `wakatime-cli` automatically when missing. To install it yourself:
```bash
brew install wakatime-cli
```
or download from [WakaTime releases](https://github.com/wakatime/wakatime-cli/releases/latest).
## How It Works
The plugin subscribes to dsh's session event firehose (`session/event`):
```mermaid
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/call` records the tool name and parsed arguments by `callId`; `tool/result`
matches it back and reads the fs tools' durable `meta` payload — 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 (`write` content, `str_replace_editor` strings).
- 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-less `assistant/attempt` records in dsh
0.1.3-alpha.1), live agent activity (`agent/status`, `agent/assistant-stream`),
turn boundaries, session disposal, and plugin teardown.
- The `--plugin` tag reports `Deepseek Harness[-<client>]/<dsh version> dsh-wakatime/<version>`.
## Development
```sh
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 wiring
- `src/config.ts` — schemastery `Config` schema, defaults, `--plugin` tag
- `src/changes.ts` — tool events → file changes, diff line counting
- `src/state.ts` — per-project rate limiting
- `src/heartbeat.ts` — `wakatime-cli` invocation, batching, flushing
- `src/cli.ts` — `wakatime-cli` discovery/download/update
- `src/paths.ts`, `src/logger.ts` — WakaTime paths and file logging
- `tests/` — unit tests plus an integration test that drives the plugin over a
real cordis `Context`
## Known Limitations
- Tool calls executed inside sandboxed/remote filesystems are tracked by their
model-visible `file_path` arguments unless the tool/result `meta` carries a
resolved absolute path (dsh 0.1.3-alpha.1 fs tools do); paths the sandbox
resolves differently may land as project-relative entities.
- `bash` commands are not attributed to files (they can touch anything).
- The dsh host version in the `--plugin` tag is `unknown` when the
`@deepseek-ai/dsh` package 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-agent` peer ranges start at
`0.1.3-alpha.1`; older hosts run the tracking paths but get no live agent
activity heartbeats.
## License
MIT — ported logic from [opencode-wakatime](https://github.com/angristan/opencode-wakatime) (MIT).
Install
dsh plugin --profile web add github:dingyi222666/dsh-wakatime
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dingyi222666-dsh-wakatime from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.