dsh-global-todo 0.9.1 → 0.11.0

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 CHANGED
@@ -1,3 +1,41 @@
1
+ ## [0.11.0] — 2026-10-20
2
+
3
+ Documentation and demo, treated as part of the product.
4
+
5
+ - **A standalone landing page** at `docs/index.html` (GitHub Pages): hero with the install line and
6
+ a 24-second captioned tour, the three claims with evidence, a five-step strip through the real
7
+ screenshots, an honest comparison against `todo_write` and kanban boards, a 3×3 grid of every
8
+ conditional state, and a footer with the data path. One file, inline CSS, no CDN, bilingual
9
+ toggle, 15 images all verified to decode.
10
+ - **`docs/screenshots/demo.mp4`** — a 24s captioned tour built from the same real renders by
11
+ `ffmpeg` (not a screen recording, and the page says so), plus the existing GIF for places that
12
+ cannot autoplay video.
13
+ - **`docs/QUICKSTART.md`** (five minutes, click by click) and **`docs/FAQ.md`** (14 real questions
14
+ answered against the code), and `docs/screenshots/README.md` indexing what each baseline shows
15
+ and what a static image cannot prove.
16
+ - **READMEs explain 0.7–0.10 at last**: steps, evidence, read-back, review, Markdown, weekly report.
17
+ English and Chinese kept as mirror images; stale claims (`21 routes`, `100+ tests`, `@0.4.0`)
18
+ corrected to 24 / 257 / 0.10.0.
19
+ - **Fixed the two mismatches the docs pass found**: the capture prompt said 200 characters where
20
+ `LIMITS.evidence` is 300, and `package.json` did not publish the new docs (so their links would
21
+ have 404'd on npm).
22
+
23
+ ## [0.10.0] — 2026-10-20
24
+
25
+ The two remaining high-value items, plus the layout bug written down.
26
+
27
+ - **Markdown in and out.** `GET /export?format=md` writes a task list (title, priority, due,
28
+ tags, evidence, description, steps, attachments as indented metadata); `POST /import` with
29
+ `{ markdown }` reads it back through `create` — new items, never an overwrite, because Markdown
30
+ has no ids. The parser is tolerant: unknown lines ignored, an unreadable date dropped, a blank
31
+ task line is not a todo.
32
+ - **Weekly agent report.** `GET /report?days=7` derives, from completion records alone: what
33
+ finished (newest first, by whom), how many conversations were involved, which files were touched
34
+ and how often, and how many still wait for review. Shown from `⋯ → 本周报告`; nothing is stored.
35
+ - **The scroll bug is documented** in `DESIGN.md` with the measurements (2236px column inside a
36
+ 680px slot, `scrollTop 0 → 0`), so the next person who touches the layout knows which two
37
+ declarations hold the contract.
38
+
1
39
  ## [0.9.1] — 2026-10-20
2
40
 
3
41
  The list could not scroll. One missing declaration, and the panel never owned its own height.
package/README.md CHANGED
@@ -18,7 +18,7 @@ Three things it does that a session-local plan cannot:
18
18
  dsh plugin add dsh-global-todo
19
19
  ```
20
20
 
21
- [中文说明](README.zh.md) · [Every state has a baseline image](#screenshots) · [Design notes](docs/DESIGN.md)
21
+ [中文说明](README.zh.md) · [5-minute quickstart](docs/QUICKSTART.md) · [FAQ](docs/FAQ.md) · [Every state has a baseline image](#screenshots) · [Design notes](docs/DESIGN.md)
22
22
 
23
23
  ---
24
24
 
@@ -39,6 +39,93 @@ one record:
39
39
  | Hand-off | — | **one click: reuse the bound conversation or open a session** |
40
40
  | Storage | session event log | one JSON file you can `cat`, edit, diff and back up |
41
41
 
42
+ ## What 0.9 and 0.10 added (and the two just before)
43
+
44
+ Six surfaces these READMEs had never explained. Session read-back and evidence came in 0.9;
45
+ Markdown and the weekly report in 0.10; steps in 0.8 and the review mark in 0.7, which until now
46
+ only existed in the changelog. Each changes what a record can carry, or what you can do with it
47
+ after the fact.
48
+
49
+ ### Steps: a plan inside the todo
50
+
51
+ A todo can carry up to 20 steps of 200 characters (`LIMITS` in `lib/model.js`). A step is
52
+ `{ text, done }` — a line inside one unit of work, not a subtask with its own id and status.
53
+
54
+ - **Add them** in "More fields" → Steps, one per line.
55
+ - **Tick them** in the detail panel; the row shows `steps 2/5`.
56
+ - **Hand off** and the dispatch prompt numbers them, prefixing finished ones with `[已完成]`.
57
+ - **Export** and Markdown writes them as nested `- [ ]` lines; import reads them back.
58
+
59
+ ### Evidence: the sentence it came from
60
+
61
+ `evidence` is the sentence a captured todo came from, quoted verbatim, up to 300 characters.
62
+ `global_todo_capture` asks the model for it, `global_todo_add` accepts it, and the detail panel
63
+ shows it as a blockquote under **In the author's words**. A todo you typed yourself has none.
64
+
65
+ The field exists for one reason: a capture without its source reads as an opinion later. It is
66
+ also in the Markdown export (`原话:`).
67
+
68
+ ### Session read-back
69
+
70
+ A hand-off starts work in another conversation, so the detail panel answers "what did it say"
71
+ without switching away. `GET /api/global-todo/session?id=` returns `alive`, `dir` and a `tail`
72
+ of the last assistant message; the panel shows **Session read-back** with running / dormant, the
73
+ last sentence, and a button that opens that conversation.
74
+
75
+ The tail is bounded and in memory: 200 conversations, 400 characters each, filled from every
76
+ `assistant/message`. Nothing is written to disk, so a Host restart forgets it. A missing tail is
77
+ normal — this process has not seen that conversation speak.
78
+
79
+ ### Review: "done" is a claim
80
+
81
+ When an agent finishes (`global_todo_done`, or the watcher on a clean turn), the record becomes
82
+ done **and** carries `review: { pending: true }`. The row gets a **Needs review** badge and the
83
+ header a **Review** chip with the count.
84
+
85
+ | Answer | Effect |
86
+ |---|---|
87
+ | **Confirm done** | stays done, the mark is dropped, the activity trail records who confirmed |
88
+ | **Send back** | back to `open`; `doneAt` and the completion record are cleared; a `reopened` activity entry is added |
89
+
90
+ Both answers are recorded as the person's action. Sending back deletes nothing: title,
91
+ description, steps, attachments and progress notes stay.
92
+
93
+ ### Markdown in and out
94
+
95
+ The JSON export is the round-trip format. Markdown is the one you read, diff and edit by hand:
96
+
97
+ ```text
98
+ - [ ] 标题
99
+ 优先级: 高 · 截止: 2026-10-21T18:00 · 标签: 发布, 文档
100
+ 原话: …
101
+ 描述: 第一行
102
+ - [x] 一步
103
+ ```
104
+
105
+ | Direction | Where | Behaviour |
106
+ |---|---|---|
107
+ | Out | **More actions → Export Markdown**, or `GET /export?format=md` | Grouped 未完成 / 已完成 / 已归档; metadata indented under each task |
108
+ | 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 |
109
+
110
+ Tolerant by design: unknown lines are ignored, an unparseable date is dropped, and a file with no
111
+ `- [ ]` line is refused with a reason. Field labels are Chinese (`优先级 / 截止 / 标签 / 原话 /
112
+ 描述 / 附件 / #`), and import reads the same set.
113
+
114
+ ### Weekly report
115
+
116
+ **More actions → This week** (`GET /report?days=7`) answers "what moved, and did any of it land
117
+ in code". Everything is derived from completion records; nothing is stored.
118
+
119
+ | Field | Meaning |
120
+ |---|---|
121
+ | `finished` | completions in the window, newest first, with the actor and the summary |
122
+ | `sessions` | distinct conversations those completions came from |
123
+ | `created` | todos added in the window |
124
+ | `pendingReview` | done items still waiting for an answer |
125
+ | `files` | file artifacts touched, most frequent first — 12 listed, `fileCount` total |
126
+
127
+ `days` above 90 is clamped to 90; a missing or non-positive value falls back to 7.
128
+
42
129
  ## Screenshots
43
130
 
44
131
  ![The panel: count chips, grouped list, urgent rows marked](docs/screenshots/panel.png)
@@ -64,6 +151,9 @@ half is rendered to static HTML with no Host and no dependencies, then photograp
64
151
  into `docs/screenshots/`. Playwright is not a dependency — the tool borrows the copy a DSH
65
152
  profile already installed, or whatever `DSH_PLAYWRIGHT` points at.
66
153
 
154
+ What each file shows, and when it is regenerated, is indexed in
155
+ [docs/screenshots/README.md](docs/screenshots/README.md).
156
+
67
157
  ## Requirements
68
158
 
69
159
  - DeepSeek Harness `0.2.x` (developed and verified against `0.2.1-rc.2`; `engines.dsh` is
@@ -90,7 +180,7 @@ The plugin is a Loader row, not a hot-loaded script: **restart the host** (`dsh
90
180
  after installing, then refresh the page. A "待办 / Todos" entry appears in the sidebar.
91
181
 
92
182
  The npm package is the source of truth. A community catalog can lag behind it or point at an
93
- older release; pin the version you want (`dsh plugin --profile web add dsh-global-todo@0.4.0`)
183
+ older release; pin the version you want (`dsh plugin --profile web add dsh-global-todo@0.10.0`)
94
184
  when it matters.
95
185
 
96
186
  To roll back:
@@ -118,8 +208,8 @@ Your list is not touched by install or uninstall: it lives outside the plugin, a
118
208
  documents and addresses (with 打开 / 复制路径 on each); read the activity trail.
119
209
  - **Undo a delete** — deleting keeps the record for eight seconds and offers 撤销, which
120
210
  restores it verbatim: same id, same creation time, same attachments.
121
- - **Keyboard** — `/` focuses search, `n` focuses the new-todo field, `Esc` collapses the
122
- open detail. Row actions appear on hover or keyboard focus.
211
+ - **Keyboard** — `/` focuses search, `n` focuses the new-todo field while the add form is
212
+ open, `Esc` collapses the open detail. Row actions appear on hover or keyboard focus.
123
213
  - **Reachable from the LAN and from a tunnel** — the routes defer to the Host's connection fence
124
214
  (which honours `--trusted-host`); the plugin's own loopback check is only the fallback for a
125
215
  Host without that service. `/health` reports `fence` so you can see which one is in force.
@@ -253,12 +343,12 @@ produces noise, and a noisy list is worse than an empty one:
253
343
  The boundary: it **records, it never starts work**. The list belongs to the human, and
254
344
  starting a task stays a human decision (see the non-goals in [ROADMAP](docs/ROADMAP.md)).
255
345
 
256
-
257
346
  Capture is not allowed to become noise. Before an item is written, the same normalized title
258
347
  (full-width, case, spacing and punctuation folded away), a close paraphrase within the last 72
259
348
  hours (Dice similarity over character bigrams, 0.8), and the same errand finished in the last 7
260
349
  days are all refused with a reason on the record; archived and cancelled rows do not block a
261
350
  new one. The rules live in `lib/capture.js` and are tested on their own.
351
+
262
352
  ### HTTP API
263
353
 
264
354
  The panel talks to the host over these routes. They are fenced by the host connection
@@ -269,9 +359,10 @@ fence when that service is present, and by a strict loopback check otherwise.
269
359
  | GET | `/api/global-todo/list` | `status`, `query`, `tag`, `sort`, `limit` — the reply carries `matched` and `truncated` when the page was cut |
270
360
  | 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) |
271
361
  | GET | `/api/global-todo/get` | `id` |
272
- | GET | `/api/global-todo/export` | — (the store's own JSON document, for backup or migration) |
362
+ | GET | `/api/global-todo/export` | — (the store's own JSON document; `?format=md` gives a Markdown task list) |
273
363
  | 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 |
274
364
  | POST | `/api/global-todo/reminders/ack` | `{ ids }` — stop listing these passed deadlines as due |
365
+ | POST | `/api/global-todo/note` | `{ id, text }` — append a progress note |
275
366
  | POST | `/api/global-todo/capture` | `{ sessionId? }` — ask the current conversation to collect its follow-ups |
276
367
  | 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 |
277
368
  | 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 |
@@ -282,6 +373,8 @@ fence when that service is present, and by a strict loopback check otherwise.
282
373
  | POST | `/api/global-todo/remove` | `{ id }` |
283
374
  | POST | `/api/global-todo/restore` | `{ todo }` — put a removed record back verbatim (undo) |
284
375
  | POST | `/api/global-todo/review` | `{ id, decision: confirm\|reject }` — answer an agent's completion |
376
+ | GET | `/api/global-todo/report?days=7` | what the agents finished in the window, and the files it touched |
377
+ | GET | `/api/global-todo/session?id=` | what a bound conversation last said (`tail`, `alive`, `dir`) |
285
378
  | POST | `/api/global-todo/link` | `{ id, add?: [...], remove?: [value \| index] }` |
286
379
  | POST | `/api/global-todo/dispatch` | `{ id, mode?: auto\|bound\|new\|current, sessionId?, dir? }` |
287
380
  | 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 |
@@ -347,7 +440,7 @@ and [docs/ROADMAP.md](docs/ROADMAP.md) for what could come next.
347
440
  npm run check # parse every file, assert the shipping shape, and — when a DSH
348
441
  # profile is present — validate every tool schema against the
349
442
  # Host's own validator
350
- npm test # 100+ unit, contract and bundle tests, zero dependencies
443
+ npm test # 257 unit, contract and bundle tests, zero dependencies
351
444
  npm run verify # both
352
445
  npm run check:matrix # the running dsh against engines.dsh, then the manifest,
353
446
  # bundle and tool-schema contracts (no preview writes)
@@ -364,6 +457,7 @@ page refresh; host-half edits need a host restart.
364
457
  ```sh
365
458
  npm run preview # render the panel to preview/panel.html (+ detail and selection variants)
366
459
  npm run screenshots # re-photograph those previews into docs/screenshots/, 1280×720
460
+ npm run tour # rebuild docs/screenshots/tour.gif from those PNGs (needs ffmpeg)
367
461
  ```
368
462
 
369
463
  The PNGs under `docs/screenshots/` ship with the package and are embedded in both
@@ -371,20 +465,30 @@ READMEs, so re-run `npm run screenshots` whenever the panel UI changes.
371
465
 
372
466
  ```text
373
467
  lib/
374
- index.js host entry: store + service + dispatcher + the three surfaces
375
- store.js one JSON file: atomic writes, serialized updates, tolerant loads
376
- model.js the todo entity: validation, patches, status, attachments, activity
377
- links.js attachment rules (pure: no filesystem access)
378
- service.js the single write path, filters and sorting
379
- routes.js the HTTP surface for the panel
380
- tools.js the ten agent tools
381
- dispatch.js handing a todo to an agent session
382
- errors.js typed failures with stable machine codes
383
- client.js the browser half (panel + sidebar entry)
384
- test/ unit, service, tool, route, dispatch, bundle and manifest tests
385
- tools/ preview.mjs — render the panel to static HTML without a Host
386
- screenshot.mjs — photograph those previews (borrows the profile's Playwright)
387
- docs/ DESIGN.md (data model and rejected alternatives), ROADMAP.md
468
+ index.js host entry: store + service + dispatcher + the optional surfaces
469
+ store.js one JSON file: atomic writes, serialized updates, tolerant loads
470
+ model.js the todo entity: validation, patches, status, attachments, steps, review, notes
471
+ links.js attachment rules (pure: no filesystem access)
472
+ service.js the single write path, filters, sorting and the dispatch prompt
473
+ routes.js the HTTP surface for the panel
474
+ tools.js the ten agent tools
475
+ dispatch.js handing a todo to an agent session (auto / bound / new / current)
476
+ completion.js the clean-turn watcher behind automatic completion
477
+ capture.js the capture duplicate gate (title, paraphrase, recent done)
478
+ ingest.js the external door and its inbox rules
479
+ markdown.js Markdown export and tolerant import
480
+ report.js the weekly aggregate over completion records
481
+ reminders.js the due rule behind the reminder strip
482
+ settings.js host-side switches and the automatic-collect policy
483
+ metrics.js in-memory counters reported by /health
484
+ diagnostics.js the bounded client error log
485
+ errors.js typed failures with stable machine codes
486
+ client.js the browser half (panel + sidebar entry)
487
+ test/ unit, service, tool, route, dispatch, bundle and manifest tests
488
+ tools/ preview.mjs — render the panel to static HTML without a Host
489
+ screenshot.mjs — photograph those previews (borrows the profile's Playwright)
490
+ tour.mjs — assemble the README tour GIF from those PNGs
491
+ docs/ DESIGN.md (data model and rejected alternatives), ROADMAP.md
388
492
  ```
389
493
 
390
494
  Contributions are welcome — read [CONTRIBUTING.md](CONTRIBUTING.md) first.
@@ -395,6 +499,7 @@ Questions, bug reports and ideas go to [Discussions](https://github.com/LUgABeEE
395
499
  When reporting a bug, include the DSH version, the plugin version from the panel footer, your OS and
396
500
  the steps that reproduce it; a screenshot of the notice is worth more than a paragraph. Security
397
501
  issues go through [SECURITY.md](SECURITY.md) instead, not the discussion board.
502
+
398
503
  ## Security
399
504
 
400
505
  The HTTP surface is local-only, the list is a local file, and no credential is ever stored.
package/README.zh.md CHANGED
@@ -14,7 +14,7 @@
14
14
  dsh plugin add dsh-global-todo
15
15
  ```
16
16
 
17
- [English](README.md) · [每个条件状态都有基线图](#截图) · [设计说明](docs/DESIGN.md)
17
+ [English](README.md) · [5 分钟上手](docs/QUICKSTART.md) · [常见问题](docs/FAQ.md) · [每个条件状态都有基线图](#截图) · [设计说明](docs/DESIGN.md)
18
18
 
19
19
  ---
20
20
 
@@ -33,6 +33,85 @@ DSH 自带的 `todo_write` 属于**某一个 agent 会话**:整表替换、三
33
33
  | 交给 Agent | 无 | **一键:复用已绑定会话,或新开一个** |
34
34
  | 存储 | 会话事件日志 | 一个 JSON 文件,能 `cat`、能手改、能 diff、能备份 |
35
35
 
36
+ ## 0.9 / 0.10 补了什么(以及再往前两版)
37
+
38
+ 六个这两份 README 一直没讲的能力:会话回读与原话是 0.9 的,Markdown 与本周报告是 0.10 的,
39
+ 步骤是 0.8 的,待确认标记是 0.7 的——后两个此前只写在 CHANGELOG 里。每一个都改变了记录能装什么,
40
+ 或者改变了事后能拿它做什么。
41
+
42
+ ### 步骤:待办里的一份计划
43
+
44
+ 一条待办最多 20 个步骤、每步 200 字(`lib/model.js` 的 `LIMITS`)。步骤是 `{ text, done }`——
45
+ 一条工作里的一行,不是有自己 id 和状态的子任务。
46
+
47
+ - **加**:新增表单的「更多字段」→ 步骤,一行一步。
48
+ - **勾**:详情里每步一个勾选框;行上显示「步骤 2/5」。
49
+ - **交出去**:派发提示词会把步骤编号,已完成的加 `[已完成]` 前缀。
50
+ - **导出**:Markdown 里写成缩进的 `- [ ]`,导入时读回来。
51
+
52
+ ### 原话:这条是从哪句话来的
53
+
54
+ `evidence` 是抓取时那条待办**来自的原话**,逐字引用,最多 300 字。`global_todo_capture` 会向模型要它,
55
+ `global_todo_add` 也收,详情里以引用块显示在「原话」下面;你自己敲的条目没有这一项。
56
+
57
+ 它只为一件事存在:没有出处的一次抓取,过几天看起来就像一句观点。Markdown 导出里也有这一行(`原话:`)。
58
+
59
+ ### 会话回读
60
+
61
+ 派发是在**另一个对话**里开工,所以详情要能回答「它说了什么」,不用切过去。
62
+ `GET /api/global-todo/session?id=` 返回 `alive`、`dir` 和最后一条 assistant 消息的 `tail`;
63
+ 详情里就是「会话回读」:在跑 / 已休眠、最后一句,以及一个打开该对话的按钮。
64
+
65
+ tail 有上限、只在内存里:200 个会话、每个 400 字,来自每条 `assistant/message`。不落盘,宿主一重启就忘。
66
+ 查不到 tail 是正常情况——这个进程没见那个对话说过话。
67
+
68
+ ### 待确认:Agent 的「做完了」只是声明
69
+
70
+ Agent 收尾时(`global_todo_done`,或干净一轮后的自动收尾),记录会变成已完成,**同时**带上
71
+ `review: { pending: true }`。行上有「待确认」徽标,顶部多一个带计数的「待确认」筛选。
72
+
73
+ | 你的回答 | 结果 |
74
+ |---|---|
75
+ | **确认完成** | 保持已完成,标记清除,动态里记下是谁确认的 |
76
+ | **退回清单** | 回到 `open`,`doneAt` 与完成记录清空,动态里加一条 `reopened` |
77
+
78
+ 两个回答都按**人**的动作记录。退回不删除任何东西:标题、描述、步骤、附件、进展记录都还在。
79
+
80
+ ### Markdown 进与出
81
+
82
+ JSON 导出是往返格式;Markdown 是拿来读、diff、手改的那份:
83
+
84
+ ```text
85
+ - [ ] 标题
86
+ 优先级: 高 · 截止: 2026-10-21T18:00 · 标签: 发布, 文档
87
+ 原话: …
88
+ 描述: 第一行
89
+ - [x] 一步
90
+ ```
91
+
92
+ | 方向 | 入口 | 行为 |
93
+ |---|---|---|
94
+ | 出 | **更多操作 → 导出 Markdown**,或 `GET /export?format=md` | 按「未完成 / 已完成 / 已归档」分组,元数据缩进在每条下面 |
95
+ | 入 | **更多操作 → 导入**(选 `.md`),或 `POST /import { markdown }` | 走正常 `create` 新建;Markdown 没有 id,所以**永远不会覆盖**已有记录 |
96
+
97
+ 解析是容错的:不认识的行直接忽略,日期解析失败就丢掉那一项,整份文件里没有一行 `- [ ]` 会明确报错。
98
+ 字段标注固定是中文(`优先级 / 截止 / 标签 / 原话 / 描述 / 附件 / #`),导入按同一套读。
99
+
100
+ ### 本周报告
101
+
102
+ **更多操作 → 本周报告**(`GET /report?days=7`)回答「这一周动了什么、有没有落到代码上」。
103
+ 全部从完成记录推出来,不存任何东西。
104
+
105
+ | 字段 | 含义 |
106
+ |---|---|
107
+ | `finished` | 窗口内完成的条目,最新在前,带完成者和说明 |
108
+ | `sessions` | 这些完成来自多少个不同的对话 |
109
+ | `created` | 窗口内新增的条数 |
110
+ | `pendingReview` | 已完成但还没人回答的条数 |
111
+ | `files` | 触及的文件,按次数排序——列 12 个,`fileCount` 是总数 |
112
+
113
+ `days` 超过 90 会被压到 90;缺省或非正数按 7 算。
114
+
36
115
  ## 截图
37
116
 
38
117
  ![面板:统计胶囊、按紧迫度分组、逾期行有强调](docs/screenshots/panel.png)
@@ -56,6 +135,8 @@ DSH 自带的 `todo_write` 属于**某一个 agent 会话**:整表替换、三
56
135
  HTML(不依赖宿主、零依赖),再以 1280×720 拍进 `docs/screenshots/`。Playwright 不是依赖——工具
57
136
  借的是 DSH profile 里已经装好的那份,或 `DSH_PLAYWRIGHT` 指向的那份。
58
137
 
138
+ 每张图显示什么状态、什么时候重新生成,见 [docs/screenshots/README.md](docs/screenshots/README.md)。
139
+
59
140
  ## 环境要求
60
141
 
61
142
  - DeepSeek Harness `0.2.x`(按 `0.2.1-rc.2` 开发与验证;`engines.dsh` 是 `>=0.2.0-rc.1 <0.3.0`,
@@ -80,7 +161,7 @@ dsh plugin --profile web add /绝对路径/dsh-global-todo
80
161
  再刷新页面。侧栏会出现「待办」入口。
81
162
 
82
163
  npm 上的包才是事实来源。社区目录可能落后于 npm、或指向某个旧 release;需要确定版本时
83
- 直接钉版本(`dsh plugin --profile web add dsh-global-todo@0.4.0`)。
164
+ 直接钉版本(`dsh plugin --profile web add dsh-global-todo@0.10.0`)。
84
165
 
85
166
  回滚:
86
167
 
@@ -105,7 +186,7 @@ dsh plugin --profile web remove dsh-global-todo
105
186
  - **详情**:就地改标题/描述/优先级/截止;挂上或摘掉文档与地址(每条都带「打开 / 复制路径」);
106
187
  查看动态(谁在什么时候做了什么)。
107
188
  - **删除可撤销**:删掉的记录保留 8 秒,点「撤销」原样恢复——同一个 id、同一个加入时间、同样的附件。
108
- - **键盘**:`/` 聚焦搜索,`n` 聚焦新建,`Esc` 收起详情;行内操作在悬停或键盘聚焦时出现。
189
+ - **键盘**:`/` 聚焦搜索;新增输入框展开后,`n` 聚焦它;`Esc` 收起详情。行内操作在悬停或键盘聚焦时出现。
109
190
  - **访问地址**:面板可以正常从宿主的**本机**、**受信任的局域网地址**(`--trusted-host` 里那些,如 `192.168.1.10:3080`)以及隧道访问——路由会交给宿主的连接围栏判断;只有宿主的围栏**不存在**时才退回「只接受本机请求」的保守检查。`/health` 里的 `fence` 会告诉你当前实际是哪一种。
110
191
  - **自动获取(可关,默认关)**:面板上的「自动获取」开关。打开后——你在某个对话里**正常收完一轮**、那个对话**安静满 30 分钟**、且**你人就在那个对话里**(不是派发目标/子 Agent/后台任务),插件才会自动问它一次「有没有要跟进的事」。中断/失败的轮次不问。**它问过之后,只有你在那个对话里再说一句话才会重新问**——因为那条提问本身就是对话里的一条消息,会重置安静计时、并让它自己的回答收尾成一轮,曾经正是这一点让同一个对话整夜追着自己反复问;冷却只是次要的阻尼。开关是**宿主状态**(面板关着也生效,`/health` 可见),关掉就完全靠你和 Agent 手动记。另外每个对话分组旁边有一个**「豁免」**按钮:点一下就把**该对话所在的工作区**整个排除在自动获取之外(再点一下取消)。这是给由机器驱动的对话准备的——比如微信机器人所在的聊天区,它们的会话重启后会换新 id,所以豁免**记在目录上而不是某一条对话上**,才不会悄悄失效。
111
192
  - **主动记**:插件会往 system prompt 里注入一段引导,列出该主动记的具体情形(跨会话的跟进、「先做 X 再 Y」、一轮收尾时发现还有一步),并明确写上「问用户要不要记是错的,直接记」。
@@ -148,8 +229,29 @@ dsh plugin --profile web remove dsh-global-todo
148
229
  | `global_todo_reopen` | 把**被误标完成**的一条标回未完成(只做 `done` / `cancelled` → `open`);本来就未完成的会被拒绝,动作按 agent 记进活动轨迹 |
149
230
  | `global_todo_remove` | 永久删除(做完的请标记,不要删) |
150
231
 
151
- 系统提示词里会注入一小段用法指引,让模型知道这份清单存在、以及它和内建 `todo_write`
152
- 的区别。不想要就把 `guidance` 设成 `false`。
232
+ ### 实际联通检查
233
+
234
+ 测试用的是替身;`npm run check:live` 检查的是**真正接上线的**宿主:面板实际会调的路由、它实际读的清单,
235
+ 以及一条「删掉再撤销」之后仍然完整的记录。它会临时建一条记录,往返后逐字段比对(id、加入时间、附件、
236
+ 标签、级别、截止、描述),确认绑定记录带 `sessionAlive` / `sessionDir`,最后即使失败也会删掉这条临时记录。
237
+ 它需要一个在跑的宿主,所以不在 `verify` 里。
238
+
239
+ ### 主动抓取
240
+
241
+ 插件会往 system prompt 里注入一段引导(用 `guidance: false` 关掉)。它把两侧都写清楚:只写「记得记事」的
242
+ 提示词只会制造噪音,而吵闹的清单比空清单更糟。
243
+
244
+ - **主动记**:用户提到需要跨会话跟进的事(等对方回复、下周要交、待确认的决定、答应别人的事)、
245
+ 一轮结束时还有一步不该在本会话继续、或用户说「记一下 / 别忘了 / 回头办」;
246
+ - **不要记**:你这一步就能做完的、本会话内的计划、纯闲聊;
247
+ - 记完用**一句话**说明记了什么,别复述整条描述;
248
+ - 用户说**「抓一下待办」**,就扫最近对话,用 `global_todo_capture` 一次登记,再回一句汇总。
249
+
250
+ 边界是:它**只负责记,从不擅自开工**。清单属于人,开不开工是人决定的(见 [ROADMAP](docs/ROADMAP.md) 里的非目标)。
251
+
252
+ 抓取不能变成噪音。写进去之前,同一个规范化标题(全半角、大小写、空格、标点都折叠掉)、
253
+ 最近 72 小时内意思很接近的转述(按字符二元组的 Dice 相似度,0.8),以及 7 天内刚做完的同一件事,
254
+ 都会被拒绝,并在记录上写出原因;已归档和已取消的条目不挡新条目。规则在 `lib/capture.js`,单独测试。
153
255
 
154
256
  ### HTTP 接口
155
257
 
@@ -161,7 +263,7 @@ dsh plugin --profile web remove dsh-global-todo
161
263
  | GET | `/api/global-todo/list` | `status`、`query`、`tag`、`sort`、`limit`——被截断时响应带 `matched` 与 `truncated` |
162
264
  | GET | `/api/global-todo/dirs` | `path?`——列子目录,供面板内置的目录浏览器使用(宿主自己的选择器在时会优先) |
163
265
  | GET | `/api/global-todo/get` | `id` |
164
- | GET | `/api/global-todo/export` | ——(存储用的那份 JSON 文档,用于备份或迁移) |
266
+ | GET | `/api/global-todo/export` | ——(存储用的那份 JSON 文档;`?format=md` 给 Markdown 任务列表) |
165
267
  | POST | `/api/global-todo/import` | `{ doc, mode?: replace\|merge, dryRun? }`——`replace` 是无损往返,`merge` 同 id 覆盖、新 id 追加(面板用的是这个);`dryRun` 只返回 `wouldCreate` / `wouldUpdate`,不落盘 |
166
268
  | POST | `/api/global-todo/reminders/ack` | `{ ids }`——这些已到期的提醒不再列出来 |
167
269
  | POST | `/api/global-todo/note` | `{ id, text }`(追加进展记录) |
@@ -175,6 +277,8 @@ dsh plugin --profile web remove dsh-global-todo
175
277
  | POST | `/api/global-todo/remove` | `{ id }` |
176
278
  | POST | `/api/global-todo/restore` | `{ todo }` —— 原样还原被删除的记录(撤销) |
177
279
  | POST | `/api/global-todo/review` | `{ id, decision: confirm\|reject }` —— 回答 Agent 的完成声明 |
280
+ | GET | `/api/global-todo/report?days=7` | 这段时间里 Agent 完成了什么、触及了哪些文件 |
281
+ | GET | `/api/global-todo/session?id=` | 绑定的会话最后说了什么(`tail` / `alive` / `dir`) |
178
282
  | POST | `/api/global-todo/link` | `{ id, add?: [...], remove?: [值 或 下标] }` |
179
283
  | POST | `/api/global-todo/dispatch` | `{ id, mode?: auto\|bound\|new\|current, sessionId?, dir? }` |
180
284
  | POST | `/api/global-todo/assign` | `{ ids, items?, target?, dir? }`——一次把多条交给 Agent;配不上的 id 原样报回,不擅自送到别处 |
@@ -235,7 +339,7 @@ epoch 与 ISO,派发提示词也带上它。因为「这事什么时候落到
235
339
  ```sh
236
340
  npm run check # 解析全部文件、校验发布形态;本机有 DSH profile 时,还会用宿主
237
341
  # 自己的校验器逐条验证工具 schema
238
- npm test # 100+ 单元/契约/bundle 测试,零依赖
342
+ npm test # 257 单元/契约/bundle 测试,零依赖
239
343
  npm run verify # 两者都跑
240
344
  npm run check:matrix # 当前 dsh 与 engines.dsh 的实际匹配,再跑 manifest/bundle/
241
345
  # 工具 schema 三类契约(不写 preview)
@@ -251,6 +355,7 @@ CI 里也没有构建产物。改 `lib/client.js` 刷新页面即生效;改宿
251
355
  ```sh
252
356
  npm run preview # 把面板渲染成 preview/panel.html(另附详情态与选择态)
253
357
  npm run screenshots # 把这些预览重新拍成 docs/screenshots/,1280×720
358
+ npm run tour # 用这些 PNG 重新拼 docs/screenshots/tour.gif(需要 ffmpeg)
254
359
  ```
255
360
 
256
361
  `docs/screenshots/` 下那几张 PNG 会随包发布、并嵌在两份 README 里,所以面板 UI 一改就要重跑
@@ -258,20 +363,30 @@ npm run screenshots # 把这些预览重新拍成 docs/screenshots/,1280×72
258
363
 
259
364
  ```text
260
365
  lib/
261
- index.js 宿主入口:store + service + dispatcher + 三个挂载面
262
- store.js 一个 JSON 文件:原子写、串行更新、容错读取
263
- model.js 待办实体:校验、补丁、状态、附件、动态
264
- links.js 附件规则(纯函数,不碰文件系统)
265
- service.js 唯一写入路径、筛选与排序
266
- routes.js 面板用的 HTTP 接口
267
- tools.js 十个 Agent 工具
268
- dispatch.js 把待办交给 agent 会话
269
- errors.js 带机器码的类型化失败
270
- client.js 浏览器半区(面板 + 侧栏入口)
271
- test/ 单元、service、工具、路由、派发、bundle、manifest 测试
272
- tools/ preview.mjs —— 不依赖宿主,把面板渲染成静态 HTML
273
- screenshot.mjs —— 把预览拍成 PNG(借 profile 里的 Playwright)
274
- docs/ DESIGN.md(数据模型与取舍)、ROADMAP.md
366
+ index.js 宿主入口:store + service + dispatcher + 可选挂载面
367
+ store.js 一个 JSON 文件:原子写、串行更新、容错读取
368
+ model.js 待办实体:校验、补丁、状态、附件、步骤、待确认、进展
369
+ links.js 附件规则(纯函数,不碰文件系统)
370
+ service.js 唯一写入路径、筛选、排序与派发提示词
371
+ routes.js 面板用的 HTTP 接口
372
+ tools.js 十个 Agent 工具
373
+ dispatch.js 把待办交给 agent 会话(auto / bound / new / current)
374
+ completion.js 干净一轮之后的自动收尾观察者
375
+ capture.js 抓取去重闸门(同名、近义转述、刚完成)
376
+ ingest.js 外部写入入口与收件箱规则
377
+ markdown.js Markdown 导出与容错导入
378
+ report.js 从完成记录推出来的周报
379
+ reminders.js 到点提醒的判定规则
380
+ settings.js 宿主侧开关与自动获取策略
381
+ metrics.js /health 里的进程内计数
382
+ diagnostics.js 有上限的面板错误日志
383
+ errors.js 带机器码的类型化失败
384
+ client.js 浏览器半区(面板 + 侧栏入口)
385
+ test/ 单元、service、工具、路由、派发、bundle、manifest 测试
386
+ tools/ preview.mjs —— 不依赖宿主,把面板渲染成静态 HTML
387
+ screenshot.mjs —— 把预览拍成 PNG(借 profile 里的 Playwright)
388
+ tour.mjs —— 用这些 PNG 拼 README 里的 tour.gif
389
+ docs/ DESIGN.md(数据模型与取舍)、ROADMAP.md
275
390
  ```
276
391
 
277
392
  欢迎贡献,先读 [CONTRIBUTING.md](CONTRIBUTING.md)。
@@ -281,6 +396,7 @@ docs/ DESIGN.md(数据模型与取舍)、ROADMAP.md
281
396
  问题、bug 和想法都发到 [Discussions](https://github.com/LUgABeEEE/dsh-global-todo/discussions)。
282
397
  报 bug 时带上 DSH 版本、面板页脚里的插件版本、系统,以及能复现的步骤;提示语截图比一段描述更有用。
283
398
  安全问题请走 [SECURITY.md](SECURITY.md),不要发在讨论区。
399
+
284
400
  ## 安全
285
401
 
286
402
  HTTP 接口只服务本机,清单是本地文件,任何凭据都不会被存储。
package/docs/DESIGN.md CHANGED
@@ -57,6 +57,27 @@ $DSH_HOME/global-todo/todos.json ← { version: 1, todos: [ … ] }
57
57
  version, and a future migration step would run before the first write, keeping the old
58
58
  file as a backup.
59
59
 
60
+ ## The panel owns its own scroll
61
+
62
+ A list longer than the slot must scroll **inside** the panel: the header and toolbar stay put, and
63
+ the rows move. That sounds obvious and was broken for two releases, because a flex column does not
64
+ do it by default.
65
+
66
+ - **The bug.** `.gt-inner` is a flex item (`flex: 1`) of `.gt-root`. A flex item's default
67
+ `min-height` is `auto`, so the column refused to shrink below its content: with a long list it
68
+ measured **2236px inside a 680px slot**, `.gt-groups` therefore never had a scroll range, and the
69
+ host's own overflow clipped everything below the fold. Measured proof: a wheel over the list left
70
+ `scrollTop` at **0**.
71
+ - **The fix is two declarations**: `.gt-inner { min-height: 0 }` (let the column shrink) and
72
+ `.gt-root { overflow: hidden }` (the panel clips, so the list is the only scroll owner). After:
73
+ `.gt-groups` is 480px of viewport over a 2036px list, and a wheel moves it 0 → 800.
74
+ - **Why it shipped.** Every screenshot in this repository is a server-rendered preview: no layout
75
+ engine, no wheel, no scrollbar. A static image cannot show that a list is stuck at the top. So the
76
+ render check now asserts the three declarations that make the contract
77
+ (`overflow: hidden` on the panel, `min-height: 0` on the column, `overflow: auto` on the list),
78
+ and anything about scrolling, focus or hover has to be measured with a real browser instead of
79
+ looked at.
80
+
60
81
  ## Reading the conversation back
61
82
 
62
83
  Handing work over used to be one-way: the panel knew a conversation was bound, and nothing about