dsh-global-todo 0.16.3 → 0.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -0
- package/README.en.md +608 -0
- package/README.md +355 -453
- package/README.zh.md +4 -470
- package/docs/DESIGN.md +21 -1
- package/docs/FAQ.md +4 -0
- package/docs/QUICKSTART.md +3 -0
- package/docs/ROADMAP.md +2 -1
- package/lib/index.js +3 -2
- package/lib/tools.js +11 -2
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +8 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,47 @@
|
|
|
1
|
+
## [0.17.1] — 2026-10-24
|
|
2
|
+
|
|
3
|
+
Two READMEs instead of one: Chinese in `README.md`, English in `README.en.md`, and the switch to
|
|
4
|
+
the other language on the first line of each.
|
|
5
|
+
|
|
6
|
+
- **Two READMEs, one language each.** `README.md` is the Chinese document — the name GitHub's
|
|
7
|
+
repository page shows and the only one npm renders — and `README.en.md` is the English one; each
|
|
8
|
+
opens with a one-line switch to the other. The 0.17.0 npm page had an English document with the
|
|
9
|
+
Chinese only reachable through a relative link, and relative links are exactly what npm rewrites
|
|
10
|
+
or drops, so the switch is an **absolute** GitHub URL (a CJK heading slug is not stable across
|
|
11
|
+
renderers either). `README.zh.md` becomes a two-line landing page so old links still resolve;
|
|
12
|
+
`README.en.md` joined `files`, and `test/manifest.test.mjs` now reads the Node and DSH floors out
|
|
13
|
+
of the two real documents. AGENTS.md records the convention.
|
|
14
|
+
|
|
15
|
+
## [0.17.0] — 2026-10-24
|
|
16
|
+
|
|
17
|
+
Documentation and prompt text: the confirmation gate is the headline now, not the third bullet.
|
|
18
|
+
Nothing in the write path changed — no field, no route, no validation rule — so upgrading is safe
|
|
19
|
+
for existing stores.
|
|
20
|
+
|
|
21
|
+
- **A release at all, because npm freezes documentation at publish time.** The README, `docs/` and
|
|
22
|
+
the plugin-page locales in the tarball are a snapshot; leading with the gate only reaches the npm
|
|
23
|
+
page and the installed docs through a version.
|
|
24
|
+
|
|
25
|
+
- **Both READMEs lead with the gate.** It is claim 01 (was 03) and it names **both doors**: an
|
|
26
|
+
agent's `global_todo_done` (or a status change to `done`), and the host closing a dispatched todo
|
|
27
|
+
after a clean turn. A new early section — 「完成确认:没有一条记录能自己变成「完成」」 / *Confirmation:
|
|
28
|
+
no record finishes itself* — carries the two-door table, the two answers and the clean-turn rule;
|
|
29
|
+
the old 0.7 subsection and its row are gone rather than duplicated, and the comparison table gained
|
|
30
|
+
a *Finishing* row.
|
|
31
|
+
- **The landing page, the npm description and the plugin-page locales open with the same sentence**:
|
|
32
|
+
page title, `meta`/`og`/`twitter` descriptions, hero lede, claim 01 (was 03), and the compare
|
|
33
|
+
table. `package.json` gained `human-in-the-loop`, `confirmation` and `approval`, which is how
|
|
34
|
+
someone looking for this actually searches.
|
|
35
|
+
- **`docs/DESIGN.md` documents `review` for the first time.** It lived in `lib/model.js` and in no
|
|
36
|
+
design doc: there is now a *Done is a claim, from either door* section, a `review` field row, and
|
|
37
|
+
the `links` row finally admits `kind: 'image'` (0.15 shipped it).
|
|
38
|
+
- **The model-facing half says it too.** The `global_todo_done` and `global_todo_update`
|
|
39
|
+
descriptions call a completion a claim; the done result prints 「这条挂在「待确认」…」; `todoLine()`
|
|
40
|
+
marks pending records in every rendering, so the model cannot read one as settled; and the injected
|
|
41
|
+
guidance tells the model to reply 「已标记完成,等你确认」 instead of 「已经结束了」. The stale
|
|
42
|
+
「插件也会在你安静下来后自动划掉」 line is gone — that idle-window heuristic was removed in 0.3.0
|
|
43
|
+
and the sentence outlived it by thirteen minor versions, telling the model the opposite of the rule.
|
|
44
|
+
|
|
1
45
|
## [0.16.3] — 2026-10-23
|
|
2
46
|
|
|
3
47
|
Documentation release: the npm page finally shows the current install instructions.
|
package/README.en.md
ADDED
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
**English** · [中文](https://github.com/LUgABeEEE/dsh-global-todo/blob/main/README.md)
|
|
2
|
+
|
|
3
|
+
# dsh-global-todo
|
|
4
|
+
|
|
5
|
+
<img src="docs/icon.svg" width="52" height="52" alt="全局待办 icon: a list card with a check and an arrow leaving it">
|
|
6
|
+
|
|
7
|
+
**A cross-session todo list for DeepSeek Harness: one file you own, written by you and your
|
|
8
|
+
agent, and handed to a conversation in one click.**
|
|
9
|
+
|
|
10
|
+
Three things it does that a session-local plan cannot — the first is the one it is known for:
|
|
11
|
+
|
|
12
|
+
1. **An agent saying "done" is a claim, whichever door it comes through.** Its own report — the
|
|
13
|
+
tool call, a status change — and the host closing the todo after a clean turn are both just
|
|
14
|
+
claims: the record waits in **待确认 / Needs review** until a person confirms it, or sends it
|
|
15
|
+
back with its completion cleared. A session-local plan never asks that question: the agent
|
|
16
|
+
rewrites the table and the work is "finished".
|
|
17
|
+
2. **One JSON file is the whole list.** `cat`, diff, commit it, put it on a NAS, carry it to
|
|
18
|
+
another machine. Nothing lives inside a session log.
|
|
19
|
+
3. **Hand-off is a button, not a paragraph.** Four modes, the working directory checked, and
|
|
20
|
+
the todo's own **steps** arrive as a numbered plan.
|
|
21
|
+
|
|
22
|
+

|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
dsh plugin add dsh-global-todo
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
[Live page](https://lugabeeee.github.io/dsh-global-todo/) (22-second typographic demo) · [中文](https://github.com/LUgABeEEE/dsh-global-todo/blob/main/README.md#zh) · [5-minute quickstart](docs/QUICKSTART.md) · [FAQ](docs/FAQ.md) · [Every state has a baseline image](#screenshots) · [Design notes](docs/DESIGN.md)
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Why another todo plugin
|
|
33
|
+
|
|
34
|
+
DSH's built-in `todo_write` belongs to one agent session: whole-list replacement, three
|
|
35
|
+
states, and a plan that disappears when the session does. The community plugins that fix
|
|
36
|
+
that split into two camps — the ones that dispatch work to agents (auto-resume, bound
|
|
37
|
+
sessions) and the ones that accept real document attachments. This plugin does both, on
|
|
38
|
+
one record:
|
|
39
|
+
|
|
40
|
+
| | built-in `todo_write` | dsh-global-todo |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| Owner | one agent session | **you** — across sessions and workspaces |
|
|
43
|
+
| Writing | whole-list replacement | per-field, human **and** agent |
|
|
44
|
+
| When it was added | not tracked | **`createdAt`, immutable, shown everywhere** |
|
|
45
|
+
| Context | title + state | description + **documents and addresses** |
|
|
46
|
+
| Hand-off | — | **one click: reuse the bound conversation or open a session** |
|
|
47
|
+
| Finishing | whatever the agent writes | **waits in Needs review for a person** |
|
|
48
|
+
| Storage | session event log | one JSON file you can `cat`, edit, diff and back up |
|
|
49
|
+
|
|
50
|
+
## Confirmation: no record finishes itself
|
|
51
|
+
|
|
52
|
+
This is the rule the plugin is known for, and the one that separates it from every list that lets
|
|
53
|
+
the agent report its own progress: **an agent's "done" is a sentence it said.** The record is
|
|
54
|
+
written as `done` and carries `review: { pending: true }`; the row shows a **Needs review** badge
|
|
55
|
+
and the header a chip with the count, until a person answers.
|
|
56
|
+
|
|
57
|
+
Both doors are gated, because both are only a claim:
|
|
58
|
+
|
|
59
|
+
| Where "done" came from | The record | Who answers |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| the agent's `global_todo_done`, or a status change to `done` | `done` **+ pending review** | a person |
|
|
62
|
+
| the dispatched conversation ended cleanly, so the host closed it (`turn/end` = `completed`) | `done` **+ pending review** | a person |
|
|
63
|
+
| you clicked done in the panel | `done` | already a person |
|
|
64
|
+
|
|
65
|
+
- **Two answers.** *Confirm done* drops the mark and records who confirmed it; *Send back* returns
|
|
66
|
+
the todo to `open`, clears `doneAt` and the completion record, and adds a `reopened` entry.
|
|
67
|
+
Nothing is deleted: title, description, steps, attachments and notes all stay.
|
|
68
|
+
- **The automatic close goes through the same gate.** What the host writes on the agent's behalf is
|
|
69
|
+
thinner than what the agent could have written — only the closing message and the files that turn
|
|
70
|
+
changed — so it has even less business counting by itself.
|
|
71
|
+
- **Only clean turns are candidates.** `error`, `aborted`, `interrupted` and `max-tokens` leave the
|
|
72
|
+
todo open. A failed job wearing a "done" label is the one failure mode that would make the whole
|
|
73
|
+
list untrustworthy.
|
|
74
|
+
|
|
75
|
+
There is exactly one implementation of the rule: `completeTodo`, `applyPatch` and a status change in
|
|
76
|
+
`lib/model.js` share the same `by === 'agent'` test, and the panel and the ten tools all go through
|
|
77
|
+
it.
|
|
78
|
+
|
|
79
|
+
## What 0.9 and 0.10 added (and the two just before)
|
|
80
|
+
|
|
81
|
+
Five surfaces these READMEs had never explained. Session read-back and evidence came in 0.9;
|
|
82
|
+
Markdown and the weekly report in 0.10; steps in 0.8. Each changes what a record can carry, or what
|
|
83
|
+
you can do with it after the fact. (The review mark came in 0.7 and now opens this document, instead
|
|
84
|
+
of hiding among the new features.)
|
|
85
|
+
|
|
86
|
+
### Steps: a plan inside the todo
|
|
87
|
+
|
|
88
|
+
A todo can carry up to 20 steps of 200 characters (`LIMITS` in `lib/model.js`). A step is
|
|
89
|
+
`{ text, done }` — a line inside one unit of work, not a subtask with its own id and status.
|
|
90
|
+
|
|
91
|
+
- **Add them** in "More fields" → Steps, one per line.
|
|
92
|
+
- **Tick them** in the detail panel; the row shows `steps 2/5`.
|
|
93
|
+
- **Hand off** and the dispatch prompt numbers them, prefixing finished ones with `[已完成]`.
|
|
94
|
+
- **Export** and Markdown writes them as nested `- [ ]` lines; import reads them back.
|
|
95
|
+
|
|
96
|
+
### Evidence: the sentence it came from
|
|
97
|
+
|
|
98
|
+
`evidence` is the sentence a captured todo came from, quoted verbatim, up to 300 characters.
|
|
99
|
+
`global_todo_capture` asks the model for it, `global_todo_add` accepts it, and the detail panel
|
|
100
|
+
shows it as a blockquote under **In the author's words**. A todo you typed yourself has none.
|
|
101
|
+
|
|
102
|
+
The field exists for one reason: a capture without its source reads as an opinion later. It is
|
|
103
|
+
also in the Markdown export (`原话:`).
|
|
104
|
+
|
|
105
|
+
### Session read-back
|
|
106
|
+
|
|
107
|
+
A hand-off starts work in another conversation, so the detail panel answers "what did it say"
|
|
108
|
+
without switching away. `GET /api/global-todo/session?id=` returns `alive`, `dir` and a `tail`
|
|
109
|
+
of the last assistant message; the panel shows **Session read-back** with running / dormant, the
|
|
110
|
+
last sentence, and a button that opens that conversation.
|
|
111
|
+
|
|
112
|
+
The tail is bounded and in memory: 200 conversations, 400 characters each, filled from every
|
|
113
|
+
`assistant/message`. Nothing is written to disk, so a Host restart forgets it. A missing tail is
|
|
114
|
+
normal — this process has not seen that conversation speak.
|
|
115
|
+
|
|
116
|
+
### Markdown in and out
|
|
117
|
+
|
|
118
|
+
The JSON export is the round-trip format. Markdown is the one you read, diff and edit by hand
|
|
119
|
+
(note that `doing` and `cancelled` come back as `open`: a hand-edited list has no work-in-progress
|
|
120
|
+
state, and the JSON remains the format that round-trips exactly):
|
|
121
|
+
|
|
122
|
+
```text
|
|
123
|
+
- [ ] 标题
|
|
124
|
+
优先级: 高 · 截止: 2026-10-21T18:00 · 标签: 发布, 文档
|
|
125
|
+
原话: …
|
|
126
|
+
描述: 第一行
|
|
127
|
+
- [x] 一步
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
| Direction | Where | Behaviour |
|
|
131
|
+
|---|---|---|
|
|
132
|
+
| Out | **More actions → Export Markdown**, or `GET /export?format=md` | Grouped 未完成 / 已完成 / 已归档; metadata indented under each task |
|
|
133
|
+
| In | **More actions → Import** (a `.md` file), or `POST /import { markdown }` | Creates new todos through `create`; Markdown has no ids, so it never overwrites an existing record |
|
|
134
|
+
|
|
135
|
+
Tolerant by design: unknown lines are ignored, an unparseable date is dropped, and a file with no
|
|
136
|
+
`- [ ]` line is refused with a reason. Field labels are Chinese (`优先级 / 截止 / 标签 / 原话 /
|
|
137
|
+
描述 / 附件 / #`), and import reads the same set.
|
|
138
|
+
|
|
139
|
+
### Weekly report
|
|
140
|
+
|
|
141
|
+
**More actions → This week** (`GET /report?days=7`) answers "what moved, and did any of it land
|
|
142
|
+
in code". Everything is derived from completion records; nothing is stored.
|
|
143
|
+
|
|
144
|
+
| Field | Meaning |
|
|
145
|
+
|---|---|
|
|
146
|
+
| `finished` | completions in the window, newest first, with the actor and the summary |
|
|
147
|
+
| `sessions` | distinct conversations those completions came from |
|
|
148
|
+
| `created` | todos added in the window |
|
|
149
|
+
| `pendingReview` | done items still waiting for an answer |
|
|
150
|
+
| `files` | file artifacts touched, most frequent first — 12 listed, `fileCount` total |
|
|
151
|
+
|
|
152
|
+
`days` above 90 is clamped to 90; a missing or non-positive value falls back to 7.
|
|
153
|
+
|
|
154
|
+
## Screenshots
|
|
155
|
+
|
|
156
|
+

|
|
157
|
+
|
|
158
|
+

|
|
159
|
+
|
|
160
|
+

|
|
161
|
+
|
|
162
|
+
Every state that only appears under a condition has a fixed baseline image, rendered by the same
|
|
163
|
+
preview and photographed by the same tool:
|
|
164
|
+
|
|
165
|
+
| State | Image |
|
|
166
|
+
|---|---|
|
|
167
|
+
| Empty list | [`panel-empty.png`](docs/screenshots/panel-empty.png) |
|
|
168
|
+
| Filter with no match | [`panel-filtered.png`](docs/screenshots/panel-filtered.png) |
|
|
169
|
+
| Inbox (awaiting confirmation) | [`panel-inbox.png`](docs/screenshots/panel-inbox.png) |
|
|
170
|
+
| Awaiting review (agent finished) | [`panel-review.png`](docs/screenshots/panel-review.png) |
|
|
171
|
+
| Due reminder | [`panel-reminders.png`](docs/screenshots/panel-reminders.png) |
|
|
172
|
+
| Selection mode | [`panel-select.png`](docs/screenshots/panel-select.png) |
|
|
173
|
+
|
|
174
|
+
These are produced by `npm run screenshots` (which chains `npm run preview`): the real client
|
|
175
|
+
half is rendered to static HTML with no Host and no dependencies, then photographed at 1280×720
|
|
176
|
+
into `docs/screenshots/`. Playwright is not a dependency — the tool borrows the copy a DSH
|
|
177
|
+
profile already installed, or whatever `DSH_PLAYWRIGHT` points at.
|
|
178
|
+
|
|
179
|
+
What each file shows, and when it is regenerated, is indexed in
|
|
180
|
+
[docs/screenshots/README.md](docs/screenshots/README.md).
|
|
181
|
+
|
|
182
|
+
## Requirements
|
|
183
|
+
|
|
184
|
+
- DeepSeek Harness `0.2.x` (developed and verified against `0.2.1-rc.2`; `engines.dsh` is
|
|
185
|
+
`>=0.2.0-rc.1 <0.3.0`, and a Host outside it skips the bundle instead of warning)
|
|
186
|
+
- Node.js `>= 22.19.0` (the Web profile's own requirement)
|
|
187
|
+
- A **Web** client: the panel is served by the Host's web server. Headless / ACP / SDK keep the
|
|
188
|
+
agent tools but have no panel; an Electron-style desktop client (file:// + IPC) is not
|
|
189
|
+
supported yet
|
|
190
|
+
- After a Host upgrade, refresh the page — the client module graph belongs to the release the
|
|
191
|
+
page booted with
|
|
192
|
+
- No runtime dependencies. Nothing is imported from `@deepseek-ai/*`.
|
|
193
|
+
|
|
194
|
+
## Install
|
|
195
|
+
|
|
196
|
+
**Check your profile name first.** The plugin is installed into one profile, not into the
|
|
197
|
+
machine; a wrong profile name still exits 0, and then nothing shows up in the sidebar after a
|
|
198
|
+
restart — the most common first-run trap.
|
|
199
|
+
|
|
200
|
+
```sh
|
|
201
|
+
ls ~/.dsh/profiles # Windows: dir %USERPROFILE%\.dsh\profiles
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Replace `<profile>` below with one of the names that command prints (`web` on most hosts;
|
|
205
|
+
use your desktop or `headless` profile name if that is what you run):
|
|
206
|
+
|
|
207
|
+
```sh
|
|
208
|
+
# from npm
|
|
209
|
+
dsh plugin --profile <profile> add dsh-global-todo
|
|
210
|
+
|
|
211
|
+
# or from a checkout, which is also the development path
|
|
212
|
+
dsh plugin --profile <profile> add /absolute/path/to/dsh-global-todo
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
The plugin is a Loader row, not a hot-loaded script: **restart the host** (`dsh web`, or
|
|
216
|
+
whatever start command you use) after installing, then refresh the page. A "待办 / Todos"
|
|
217
|
+
entry appears in the sidebar.
|
|
218
|
+
|
|
219
|
+
> Sidebar still empty? Check three things in order: ① which profiles `ls` actually printed and
|
|
220
|
+
> whether the one you installed into is the one running; ② whether the host really restarted
|
|
221
|
+
> (the Loader is read once at boot); ③ whether the page was refreshed (the client module graph
|
|
222
|
+
> belongs to the release the page booted with).
|
|
223
|
+
|
|
224
|
+
The npm package is the source of truth. A community catalog can lag behind it or point at an
|
|
225
|
+
older release; pin the version you want (`dsh plugin --profile <profile> add dsh-global-todo@0.13.1`)
|
|
226
|
+
when it matters.
|
|
227
|
+
|
|
228
|
+
### Installing from DSH's Discover tab
|
|
229
|
+
|
|
230
|
+
DSH's Discover tab reads a plugin market index from a URL you add yourself — dsh ships none, and
|
|
231
|
+
a user-added index is the one path that installs without copying a command. This repository
|
|
232
|
+
publishes one:
|
|
233
|
+
|
|
234
|
+
```text
|
|
235
|
+
https://lugabeeee.github.io/dsh-global-todo/market/index.json
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
It is generated from npm by `npm run market` (`docs/market/index.json`) and validated against
|
|
239
|
+
dsh's own `@deepseek-ai/dsh-plugin-market-index` before it is written. `npm test` fails when its
|
|
240
|
+
newest version is not the version in `package.json`, so a release that forgets to regenerate it
|
|
241
|
+
is caught before it ships.
|
|
242
|
+
|
|
243
|
+
**Hand-copied a directory instead?** Check it before wondering why the plugins page looks wrong:
|
|
244
|
+
|
|
245
|
+
```sh
|
|
246
|
+
npx dsh-global-todo-doctor "$HOME/.dsh/plugins-local/全局待办插件"
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
It verifies the manifest, the patch file, both entries, `locale/`, the icon and `lib/`, and exits
|
|
250
|
+
non-zero when something required is missing — the `locale/` case is the field failure that makes
|
|
251
|
+
the desktop app print `Plugin metadata … ENOENT … \locale` while the plugin still loads.
|
|
252
|
+
|
|
253
|
+
To roll back:
|
|
254
|
+
|
|
255
|
+
```sh
|
|
256
|
+
dsh plugin --profile <profile> remove dsh-global-todo
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Your list is not touched by install or uninstall: it lives outside the plugin, at
|
|
260
|
+
`$DSH_HOME/global-todo/todos.json`.
|
|
261
|
+
|
|
262
|
+
## Using it
|
|
263
|
+
|
|
264
|
+
### The panel
|
|
265
|
+
|
|
266
|
+
- **Add** — type a title and press Enter. "More fields" reveals the description, the
|
|
267
|
+
attachment box (one document path or URL per line), tags, priority and a due time. An
|
|
268
|
+
attachment may also be `{ kind: "image", value: "/abs/shot.png" }` — the row then shows a
|
|
269
|
+
thumbnail and the detail a preview, with the bytes read on demand (png/jpg/jpeg/webp/gif/avif,
|
|
270
|
+
≤8 MB) and never copied into the list.
|
|
271
|
+
- **Filters** — all / open / doing / done / overdue, plus a text search across titles,
|
|
272
|
+
descriptions, tags and attachments. The count chips in the header are filters too.
|
|
273
|
+
- **Grouped by urgency** — overdue / today / next up / later / no due date / done, each
|
|
274
|
+
collapsible, because "what is late" is the first question a list has to answer.
|
|
275
|
+
- **Rows** — show `added <time> (<age>)` with the exact timestamp on hover, the priority,
|
|
276
|
+
the due time, the tags, the attachment count, and which side created the entry.
|
|
277
|
+
- **Details** — edit title, description, priority and due time inline; attach or detach
|
|
278
|
+
documents and addresses (with 打开 / 复制路径 on each); read the activity trail.
|
|
279
|
+
- **Undo a delete** — deleting keeps the record for eight seconds and offers 撤销, which
|
|
280
|
+
restores it verbatim: same id, same creation time, same attachments.
|
|
281
|
+
- **Keyboard** — `/` focuses search, `n` focuses the new-todo field while the add form is
|
|
282
|
+
open, `Esc` collapses the open detail. Row actions appear on hover or keyboard focus.
|
|
283
|
+
- **Reachable from the LAN and from a tunnel** — the routes defer to the Host's connection fence
|
|
284
|
+
(which honours `--trusted-host`); the plugin's own loopback check is only the fallback for a
|
|
285
|
+
Host without that service. `/health` reports `fence` so you can see which one is in force.
|
|
286
|
+
- **Auto-collect (off by default)** — a panel switch. When on, a conversation you are in that
|
|
287
|
+
finishes a clean turn is asked once whether anything needs following up, but only once it has
|
|
288
|
+
been quiet for 30 minutes, and only if you are actually in that conversation (never a dispatch
|
|
289
|
+
target, a subagent or a background run). Aborted and failed turns are skipped. The question is
|
|
290
|
+
itself a message in the conversation, so it restarts the quiet clock — which is why **only a
|
|
291
|
+
message you wrote arms it again**: one question per turn from you, and it can never chase its own
|
|
292
|
+
answer. The switch is Host state, so it survives the panel being closed and is reported by
|
|
293
|
+
`/health`. Each conversation's group also carries an **`Exempt`** button: it takes that
|
|
294
|
+
conversation's whole **working directory** out of automatic collect (click again to undo). That
|
|
295
|
+
is for conversations a machine drives — a chat companion's room — whose session is recreated with
|
|
296
|
+
a new id when it restarts, so the exemption is keyed on the directory rather than on one
|
|
297
|
+
conversation and cannot quietly stop applying.
|
|
298
|
+
- **Collect per conversation** — the `Collect` action sits on each conversation's group header in
|
|
299
|
+
the list, asks *that* conversation to record what needs following up, and the items land under
|
|
300
|
+
it. The toolbar keeps a "collect from this chat" fallback, shown only when the current
|
|
301
|
+
conversation has no group yet. A conversation that is not running is resumed first, like a
|
|
302
|
+
hand-off.
|
|
303
|
+
- **Batch assignment** — switch on `选择`, tick a few todos, and one click sends each one to the
|
|
304
|
+
conversation that wrote it, else to a same-workspace conversation, else to a target you pick.
|
|
305
|
+
That is an attempt order: an unloadable target means the next one is tried. Anything with
|
|
306
|
+
nowhere to go comes back with a picker rather than being sent somewhere unchosen, and one
|
|
307
|
+
failure does not abort the batch.
|
|
308
|
+
- **Search syntax** — `tag:release` (or `#ops`), `is:overdue`, `is:active`, `has:link`, `before:2026-11-01` / `after:2026-10-01` (deadline bounds; a half-typed date simply stops filtering),
|
|
309
|
+
`has:note`, `level:p0`, plus free words combined with AND (which also match note text). A
|
|
310
|
+
search without a colon behaves exactly as it always did.
|
|
311
|
+
- **Progress notes** — an append-only trail per todo (count on the row, full text and an add
|
|
312
|
+
box in the detail). An agent reporting a blocker writes here instead of rewriting your
|
|
313
|
+
description, and writing one makes automatic completion stand down.
|
|
314
|
+
- **Dead attachments are flagged** — a file link whose target is gone is marked on the row and
|
|
315
|
+
labelled in the detail. Computed on read, never stored.
|
|
316
|
+
- **Where a new conversation goes** — a hand-off that opens one joins the workspace owning
|
|
317
|
+
the conversation that wrote the todo (and starts in that workspace's directory), so
|
|
318
|
+
"continue this work" does not land beside unrelated sessions. The picker also lists the
|
|
319
|
+
workspaces (`+ new conversation · <workspace>`), and a named workspace wins over the
|
|
320
|
+
inherited one. The working directory only decides when neither is known.
|
|
321
|
+
- **The picker lists what you actually use, newest first, without archived conversations.** The
|
|
322
|
+
order is the sidebar's recency key (`updatedAt`). Conversations sharing a title get a short id
|
|
323
|
+
suffix, and a target that is no longer listed (archived since it was chosen) is shown as
|
|
324
|
+
`(archived)` instead of appearing as a different conversation.
|
|
325
|
+
- **You pick the target** — the toolbar shows `交给:[ conversation ▾ ]`, listing your
|
|
326
|
+
conversations: whichever you choose is sent with `authoritative: true` and the Host honours
|
|
327
|
+
it verbatim, with no inference on top. The list marks the conversation the Host would have
|
|
328
|
+
chosen on its own, `自动` restores that behaviour, and `+ a new conversation` starts a fresh
|
|
329
|
+
context. A choice is remembered, and changing it is one click in the same control — what you
|
|
330
|
+
see is what happens.
|
|
331
|
+
- **A receipt for every hand-off** — the panel says which conversation received the work and
|
|
332
|
+
offers to open it. A dispatch starts work *elsewhere*, so staying silent looked exactly
|
|
333
|
+
like "nothing happened".
|
|
334
|
+
- **Hand to agent** — the panel says **where it will go** before you press it, and the
|
|
335
|
+
default is `current`: the conversation **you** last wrote in (read from the session log,
|
|
336
|
+
keeping only `source.kind === 'user'` messages — the plugin's own injected hand-off
|
|
337
|
+
messages are excluded, or each dispatch would drag the next one to its own target; the id
|
|
338
|
+
is also kept next to the data so a restart does not forget it). `auto` prefers the
|
|
339
|
+
conversation this todo already owns, then the current one, and only then opens a fresh
|
|
340
|
+
session; `new` always opens one; `bound` insists on the bound conversation and says why
|
|
341
|
+
if it is gone. When nothing usable can be resolved the hand-off **fails with a reason**
|
|
342
|
+
instead of quietly opening another conversation.
|
|
343
|
+
- **Grouping** — the toolbar offers `conversation / time / level` and defaults to
|
|
344
|
+
**conversation**: everything one agent conversation submitted collapses into a single
|
|
345
|
+
entry that says how many it holds, so a few 「抓一下待办」 runs do not turn the list into a
|
|
346
|
+
wall of rows. A conversation with one item shows that row directly; items with no source
|
|
347
|
+
conversation (added in the panel) sit under 手动添加. `time` is the original
|
|
348
|
+
overdue / today / soon / later / no-due buckets, `level` groups by priority.
|
|
349
|
+
- **Levels P0–P3** — `P0 urgent / P1 high / P2 normal (default) / P3 low`; the API accepts
|
|
350
|
+
either the canonical names or the labels (`priority: "P0"` works), with a badge on the
|
|
351
|
+
row. Records written before levels existed keep their `high/normal/low`, so nothing needed a
|
|
352
|
+
migration. A conversation group shows the highest level it holds, and a green dot marks a
|
|
353
|
+
group with a conversation still running.
|
|
354
|
+
- **Collect from this chat** — one button injects "record what this conversation needs to
|
|
355
|
+
follow up, with `global_todo_capture`" into the conversation you are in, so a chat turns
|
|
356
|
+
into todos without typing them out. The prompt states what *not* to record as well
|
|
357
|
+
(session plans, small talk, finished work) — a capture instruction without that ceiling
|
|
358
|
+
only produces noise.
|
|
359
|
+
- **Automatic completion** — when a handed-off session closes its turn **cleanly**, the Host
|
|
360
|
+
finishes the todo itself: the agent's closing message becomes the summary and the files
|
|
361
|
+
that turn touched become the artifacts. No waiting for the model to remember a tool call.
|
|
362
|
+
A finished row shows 完成于 … plus artifact chips, and its detail carries a completion
|
|
363
|
+
section you can open or copy paths from. Two guards matter: only a session this plugin
|
|
364
|
+
**just dispatched to** can complete itself (idle chatter in a bound conversation must not
|
|
365
|
+
finish work), and a turn that **failed or was interrupted is never marked done** — an open
|
|
366
|
+
todo is far better than a failed job wearing a "done" label. `global_todo_done` with
|
|
367
|
+
`summary` and `artifacts` remains the primary path; the watcher is the safety net.
|
|
368
|
+
- **Working directory** — the toolbar says where a new session will start. It follows the
|
|
369
|
+
conversation you are in by default; 选择 picks one explicitly and remembers it, and
|
|
370
|
+
跟随当前会话 clears that choice. A todo also remembers the directory it was written
|
|
371
|
+
from (taken from the tool call's own context), and that is inherited too. With none of
|
|
372
|
+
the three available, the hand-off is **refused with a reason** — a session without a
|
|
373
|
+
working directory is a session where the agent can do nothing.
|
|
374
|
+
|
|
375
|
+
### Agent tools
|
|
376
|
+
|
|
377
|
+
| Tool | Purpose |
|
|
378
|
+
|---|---|
|
|
379
|
+
| `global_todo_add` | Record an item — description, attachments, steps, tags, priority, due time |
|
|
380
|
+
| `global_todo_note` | Append a progress note — where you are, what blocks you, who must decide |
|
|
381
|
+
| `global_todo_capture` | **Record a batch**: for 「抓一下待办」 or a turn that produced several follow-ups; each entry is validated on its own, and entries scoring below `captureMinWeight` are reported as skipped with their weight instead of being registered |
|
|
382
|
+
| `global_todo_list` | Query by state, tag or text; every row carries its creation time |
|
|
383
|
+
| `global_todo_get` | Read one item in full, including attachments and the last hand-off |
|
|
384
|
+
| `global_todo_update` | Change any field of one item, including `status` (`archived` archives without deleting); `""` clears, `[]` empties |
|
|
385
|
+
| `global_todo_link` | Attach or detach documents and addresses |
|
|
386
|
+
| `global_todo_done` | Mark done, back to open, or in progress |
|
|
387
|
+
| `global_todo_reopen` | Put a **wrongly finished** item back (`done` / `cancelled` → `open`); refuses anything else, and the trail records it as the agent |
|
|
388
|
+
| `global_todo_remove` | Delete permanently (done items should be marked, not deleted) |
|
|
389
|
+
|
|
390
|
+
### Live check
|
|
391
|
+
|
|
392
|
+
The test suite uses doubles; `npm run check:live` checks the **wired** Host instead —
|
|
393
|
+
the routes the panel actually calls, the list it actually reads, and a record that survives
|
|
394
|
+
delete-then-undo. It creates a temporary record, compares every field after the round trip
|
|
395
|
+
(id, creation time, attachments, tags, level, due time, description), asserts that bound
|
|
396
|
+
records carry `sessionAlive`/`sessionDir`, and removes its temporary record even when it
|
|
397
|
+
fails. It needs a running Host, so it is not part of `verify`.
|
|
398
|
+
|
|
399
|
+
### Proactive capture
|
|
400
|
+
|
|
401
|
+
A guidance section is injected into the system prompt (turn it off with `guidance: false`).
|
|
402
|
+
It states both sides of the policy, because a prompt that only says "remember things"
|
|
403
|
+
produces noise, and a noisy list is worse than an empty one:
|
|
404
|
+
|
|
405
|
+
- **record on your own initiative** when the user mentions something that needs following
|
|
406
|
+
up across sessions (waiting on a reply, due next week, a decision pending, a promise),
|
|
407
|
+
when a turn ends with a step that should not continue in that session, or when the user
|
|
408
|
+
says 「记一下 / 别忘了 / 回头办」;
|
|
409
|
+
- **do not record** what you can finish in this step, the session's own plan, or small talk;
|
|
410
|
+
- say in **one line** what you recorded — do not restate the description;
|
|
411
|
+
- say **「抓一下待办」** and the agent scans the recent conversation, records the batch with
|
|
412
|
+
`global_todo_capture` and answers with a summary.
|
|
413
|
+
|
|
414
|
+
The boundary: it **records, it never starts work**. The list belongs to the human, and
|
|
415
|
+
starting a task stays a human decision (see the non-goals in [ROADMAP](docs/ROADMAP.md)).
|
|
416
|
+
|
|
417
|
+
Capture is not allowed to become noise. Before an item is written, the same normalized title
|
|
418
|
+
(full-width, case, spacing and punctuation folded away), a close paraphrase within the last 72
|
|
419
|
+
hours (Dice similarity over character bigrams, 0.8), and the same errand finished in the last 7
|
|
420
|
+
days are all refused with a reason on the record; archived and cancelled rows do not block a
|
|
421
|
+
new one. The rules live in `lib/capture.js` and are tested on their own.
|
|
422
|
+
|
|
423
|
+
### HTTP API
|
|
424
|
+
|
|
425
|
+
The panel talks to the host over these routes. They are fenced by the host connection
|
|
426
|
+
fence when that service is present, and by a strict loopback check otherwise.
|
|
427
|
+
|
|
428
|
+
| Method | Path | Body / query |
|
|
429
|
+
|---|---|---|
|
|
430
|
+
| GET | `/api/global-todo/list` | `status`, `query`, `tag`, `sort`, `limit` — the reply carries `matched` and `truncated` when the page was cut |
|
|
431
|
+
| GET | `/api/global-todo/dirs` | `path?` — list subdirectories for the panel's built-in folder picker (the shell's own picker is preferred when mounted) |
|
|
432
|
+
| GET | `/api/global-todo/get` | `id` |
|
|
433
|
+
| GET | `/api/global-todo/export` | — (the store's own JSON document; `?format=md` gives a Markdown task list) |
|
|
434
|
+
| POST | `/api/global-todo/import` | `{ doc, mode?: replace\|merge, dryRun? }` — `replace` is an exact round trip, `merge` updates by id and appends (what the panel sends); `dryRun` returns `wouldCreate` / `wouldUpdate` and writes nothing |
|
|
435
|
+
| POST | `/api/global-todo/reminders/ack` | `{ ids }` — stop listing these passed deadlines as due |
|
|
436
|
+
| POST | `/api/global-todo/note` | `{ id, text }` — append a progress note |
|
|
437
|
+
| POST | `/api/global-todo/capture` | `{ sessionId? }` — ask the current conversation to collect its follow-ups |
|
|
438
|
+
| POST | `/api/global-todo/bulk` | `{ ids, status?, addTags?, removeTags? }` — one change across a selection; a stale id lands in `unmatched` instead of failing the batch |
|
|
439
|
+
| POST | `/api/global-todo/ingest` | `{ items: [{ title, source: { kind }, confidence }], dryRun? }` with `Authorization: Bearer <settings.ingestToken>` — external items; below `ingestConfirmBelow` they wait in the inbox |
|
|
440
|
+
| GET | `/api/global-todo/health` | — (store location, counts, dispatch capabilities, plus **tool registration** and **whether the completion watcher attached** — both fail quietly, so both are reported — and `metrics`: dispatch success rate, agent tool calls, list growth; `autoCollect`: why each waiting conversation was or was not asked, counters, and how the settings file loaded; `version` is the running module and `packageVersion`/`stale` say whether the installed package is newer than the running one; `review.items` names the finished todos that still wait for a person, so a check does not have to drive the panel) |
|
|
441
|
+
| POST | `/api/global-todo/create` | `{ title, description?, links?, steps?, tags?, priority?, dueAt? }` |
|
|
442
|
+
| POST | `/api/global-todo/update` | `{ id, ...fields, by?: human\|agent }` |
|
|
443
|
+
| POST | `/api/global-todo/status` | `{ id, status, by?: human\|agent }` |
|
|
444
|
+
| POST | `/api/global-todo/remove` | `{ id }` |
|
|
445
|
+
| POST | `/api/global-todo/restore` | `{ todo }` — put a removed record back verbatim (undo) |
|
|
446
|
+
| POST | `/api/global-todo/review` | `{ id, decision: confirm\|reject }` — answer an agent's completion |
|
|
447
|
+
| GET | `/api/global-todo/report?days=7` | what the agents finished in the window, and the files it touched |
|
|
448
|
+
| GET | `/api/global-todo/session?id=` | what a bound conversation last said (`tail`, `alive`, `dir`) |
|
|
449
|
+
| POST | `/api/global-todo/relink` | `{ id, index, value }` — re-point a dead attachment (the value must still be a valid absolute path or URL of that kind) |
|
|
450
|
+
| GET | `/api/global-todo/asset?id=&index=` | the bytes of an **image** attachment (path read from the store, never from the request; ≤8 MB, png/jpg/jpeg/webp/gif/avif) |
|
|
451
|
+
| POST | `/api/global-todo/link` | `{ id, add?: [...], remove?: [value \| index] }` |
|
|
452
|
+
| POST | `/api/global-todo/dispatch` | `{ id, mode?: auto\|bound\|new\|current, sessionId?, dir? }` |
|
|
453
|
+
| POST | `/api/global-todo/assign` | `{ ids, items?, target?, dir? }` — hand several todos over in one call; an id that matches nothing comes back instead of being sent elsewhere |
|
|
454
|
+
| POST | `/api/global-todo/settings` | `{ autoCollect?, idleMinutes?, cooldownMinutes?, exemptWorkspaces?, captureMinWeight? }` — the Host-side switches behind automatic collect; `captureMinWeight` (0–1, default 0.35) is the floor below which a batch-capture candidate is reported as skipped instead of registered |
|
|
455
|
+
| POST | `/api/global-todo/diagnostics` | `{ scope, status?, reason?, message?, url? }` — the panel's own error report |
|
|
456
|
+
|
|
457
|
+
`by` on `/update` and `/status` says whose write this is: omitted (what the panel sends) it is
|
|
458
|
+
the person at the keyboard, and a script acting for the agent says `by: 'agent'` rather than
|
|
459
|
+
being filed as a human. Anything else is a `400` — a provenance field that silently absorbs a
|
|
460
|
+
typo is worse than no field at all.
|
|
461
|
+
|
|
462
|
+
Errors are typed: `400` for a rejected value, `404` for an unknown id, `413` for an
|
|
463
|
+
oversized body, `403` for a request that fails the fence, `502` when a hand-off cannot
|
|
464
|
+
reach the agents service. The body is always `{ ok, ... }` with a machine `reason`.
|
|
465
|
+
|
|
466
|
+
## Where it is listed
|
|
467
|
+
|
|
468
|
+
npm is the only install source; every registry below is a discovery surface. Verified 2026-10-21 —
|
|
469
|
+
re-check with `node tools/listings.mjs` rather than trusting this list.
|
|
470
|
+
|
|
471
|
+
| Registry | Form | State |
|
|
472
|
+
|---|---|---|
|
|
473
|
+
| [npm](https://www.npmjs.com/package/dsh-global-todo) | the package | `0.11.2` |
|
|
474
|
+
| [1024 Store](https://deepseek1024.com) | catalog entry ([PR #654](https://github.com/imsai-sh/awesome-deepseek-harness-plugins/pull/654), merged) | listed; its install line still resolves to the GitHub source |
|
|
475
|
+
| [dsh.pub](https://dsh.pub/en/plugins/dsh-global-todo/) | published page + `dshpub` CLI | live, pinned to a commit |
|
|
476
|
+
| [DSH-Plugins-Marketplace](https://github.com/bradeGithub/DSH-Plugins-Marketplace) | `registry.json`, collected from the `dsh-plugin` topic | `v0.11.2`, risk tier `safe` |
|
|
477
|
+
| [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) (19.7k★) | one YAML per plugin | merged ([PR #7498](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/pull/7498)) |
|
|
478
|
+
| [walkinglabs](https://github.com/walkinglabs/awesome-deepseek-harness-plugins) | source-verified README | merged ([PR #114](https://github.com/walkinglabs/awesome-deepseek-harness-plugins/pull/114)) |
|
|
479
|
+
|
|
480
|
+
Five more are in review (Zhiyuan-Fan, kejixiaoliang, beancookie, dshworks, losebird) and one
|
|
481
|
+
maintainer promised to include it (dshopen.org). The full table, including the auto-collected
|
|
482
|
+
topic feeds we have not verified, is [docs/DISTRIBUTION.md](docs/DISTRIBUTION.md).
|
|
483
|
+
|
|
484
|
+
## Where the data lives
|
|
485
|
+
|
|
486
|
+
`$DSH_HOME/global-todo/todos.json`, or any absolute path you set:
|
|
487
|
+
|
|
488
|
+
```yaml
|
|
489
|
+
# the profile's cordis.patch.yml
|
|
490
|
+
- id: global-todo
|
|
491
|
+
config:
|
|
492
|
+
storePath: /home/me/notes/global-todos.json # a synced or versioned folder
|
|
493
|
+
dispatchDir: /home/me/projects/thing # default cwd for a new session
|
|
494
|
+
requireExistingFiles: true # refuse a document that is not there yet
|
|
495
|
+
toolGroups: all # all (10 tools) | core (6: the list itself) | none
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
The file is the artifact: plain JSON, one record per todo, written through a temp file and
|
|
499
|
+
`rename()`, with a write lock that serializes the human's and the agent's edits. A damaged
|
|
500
|
+
file is reported (`store-corrupt`) rather than silently reset, and a single mangled record
|
|
501
|
+
is skipped instead of taking the list down. Back it up by copying it.
|
|
502
|
+
|
|
503
|
+
## Design decisions
|
|
504
|
+
|
|
505
|
+
**Attachments are references, not uploads.** A document is an absolute path that stays
|
|
506
|
+
where it is; an address is a URL. Nothing is copied into plugin storage, so a document
|
|
507
|
+
stays editable and diffable, the list stays small, and a dispatched agent opens the very
|
|
508
|
+
same path with its own tools. URLs carrying credentials are refused, because the list, the
|
|
509
|
+
audit trail and every prompt would otherwise keep a copy of the secret.
|
|
510
|
+
|
|
511
|
+
**Creation time is first-class.** `createdAt` is set once and never rewritten. The panel
|
|
512
|
+
shows it, the tools return it as both epoch and ISO, and the dispatch prompt includes it,
|
|
513
|
+
because "when did this land on my plate" is half of what a global todo is for.
|
|
514
|
+
|
|
515
|
+
**One write path.** The panel and the tools both call the same service functions, so a
|
|
516
|
+
validation rule, an audit entry or the write lock cannot exist on one side only.
|
|
517
|
+
|
|
518
|
+
**A failed hand-off is louder than a successful one.** Success and failure are both
|
|
519
|
+
recorded on the todo, and the panel shows the failure with its reason. A dispatch that
|
|
520
|
+
silently did nothing is worse than one that never happened.
|
|
521
|
+
|
|
522
|
+
**Optional services degrade, they do not crash.** `webServer`, `tools`, `agents`,
|
|
523
|
+
`agentPresets`, `workspaceRegistry` and `agentDefaultModel` are all reached through
|
|
524
|
+
`ctx.inject` or a guarded `ctx.get`. A lean composition loses exactly that surface.
|
|
525
|
+
|
|
526
|
+
See [docs/DESIGN.md](docs/DESIGN.md) for the data model and the rejected alternatives,
|
|
527
|
+
and [docs/ROADMAP.md](docs/ROADMAP.md) for what could come next.
|
|
528
|
+
|
|
529
|
+
## Development
|
|
530
|
+
|
|
531
|
+
```sh
|
|
532
|
+
npm run check # parse every file, assert the shipping shape, and — when a DSH
|
|
533
|
+
# profile is present — validate every tool schema against the
|
|
534
|
+
# Host's own validator
|
|
535
|
+
npm test # unit, contract and bundle tests, zero dependencies
|
|
536
|
+
npm run verify # static checks + tests
|
|
537
|
+
npm run check:contracts # the cross-field matrix a reviewer re-tests by hand:
|
|
538
|
+
# one invalid value, one answer, whichever field it lands on
|
|
539
|
+
npm run check:windows # the same idea on a real filesystem (Windows in CI)
|
|
540
|
+
npm run check:matrix # the running dsh against engines.dsh, then the manifest,
|
|
541
|
+
# bundle and tool-schema contracts (no preview writes)
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
`engines.dsh` is the one version fact that matters: a Host outside it skips the whole bundle
|
|
545
|
+
instead of warning, so `check:matrix` is the smoke to run before widening the range.
|
|
546
|
+
|
|
547
|
+
There is **no build step**. The host half is plain ESM under `lib/`; the client half is a
|
|
548
|
+
classic script that registers itself with `window.__ModuleLoader__`, so no bundler,
|
|
549
|
+
TypeScript toolchain or CI artifact is involved. `lib/client.js` edits take effect on a
|
|
550
|
+
page refresh; host-half edits need a host restart.
|
|
551
|
+
|
|
552
|
+
```sh
|
|
553
|
+
npm run preview # render the panel to preview/panel.html (+ detail and selection variants)
|
|
554
|
+
npm run screenshots # re-photograph those previews into docs/screenshots/, 1280×720
|
|
555
|
+
npm run tour # rebuild docs/screenshots/tour.gif from those PNGs (needs ffmpeg)
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
The PNGs under `docs/screenshots/` ship with the package and are embedded in both
|
|
559
|
+
READMEs, so re-run `npm run screenshots` whenever the panel UI changes.
|
|
560
|
+
|
|
561
|
+
```text
|
|
562
|
+
lib/
|
|
563
|
+
index.js host entry: store + service + dispatcher + the optional surfaces
|
|
564
|
+
store.js one JSON file: atomic writes, serialized updates, tolerant loads
|
|
565
|
+
model.js the todo entity: validation, patches, status, attachments, steps, review, notes
|
|
566
|
+
links.js attachment rules (pure: no filesystem access)
|
|
567
|
+
service.js the single write path, filters, sorting and the dispatch prompt
|
|
568
|
+
routes.js the HTTP surface for the panel
|
|
569
|
+
tools.js the ten agent tools
|
|
570
|
+
dispatch.js handing a todo to an agent session (auto / bound / new / current)
|
|
571
|
+
completion.js the clean-turn watcher behind automatic completion
|
|
572
|
+
capture.js the capture duplicate gate (title, paraphrase, recent done)
|
|
573
|
+
ingest.js the external door and its inbox rules
|
|
574
|
+
markdown.js Markdown export and tolerant import
|
|
575
|
+
report.js the weekly aggregate over completion records
|
|
576
|
+
reminders.js the due rule behind the reminder strip
|
|
577
|
+
settings.js host-side switches and the automatic-collect policy
|
|
578
|
+
metrics.js in-memory counters reported by /health
|
|
579
|
+
diagnostics.js the bounded client error log
|
|
580
|
+
errors.js typed failures with stable machine codes
|
|
581
|
+
client.js the browser half (panel + sidebar entry)
|
|
582
|
+
test/ unit, service, tool, route, dispatch, bundle and manifest tests
|
|
583
|
+
tools/ preview.mjs — render the panel to static HTML without a Host
|
|
584
|
+
screenshot.mjs — photograph those previews (borrows the profile's Playwright)
|
|
585
|
+
tour.mjs — assemble the README tour GIF from those PNGs
|
|
586
|
+
docs/ DESIGN.md (data model and rejected alternatives), ROADMAP.md
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
Contributions are welcome — read [CONTRIBUTING.md](CONTRIBUTING.md) first.
|
|
590
|
+
|
|
591
|
+
## Feedback
|
|
592
|
+
|
|
593
|
+
Questions, bug reports and ideas go to [Discussions](https://github.com/LUgABeEEE/dsh-global-todo/discussions).
|
|
594
|
+
When reporting a bug, include the DSH version, the plugin version from the panel footer, your OS and
|
|
595
|
+
the steps that reproduce it; a screenshot of the notice is worth more than a paragraph. Security
|
|
596
|
+
issues go through [SECURITY.md](SECURITY.md) instead, not the discussion board.
|
|
597
|
+
|
|
598
|
+
## Security
|
|
599
|
+
|
|
600
|
+
The HTTP surface is local-only, the list is a local file, and no credential is ever stored.
|
|
601
|
+
Please report vulnerabilities as described in [SECURITY.md](SECURITY.md).
|
|
602
|
+
|
|
603
|
+
## License
|
|
604
|
+
|
|
605
|
+
[MIT](LICENSE) © 2026 LUgABeEEE
|
|
606
|
+
|
|
607
|
+
A community plugin, not a DeepSeek project: the `dsh` prefix marks the ecosystem it plugs into, not
|
|
608
|
+
an endorsement. "DeepSeek" and "DeepSeek Harness" remain their owners' marks.
|