@dennisrongo/dsh-todo 0.2.0 → 0.4.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/README.md +253 -17
- package/lib/bin.js +504 -0
- package/lib/cli.js +513 -0
- package/lib/client.js +286 -48
- package/lib/index.js +197 -81
- package/lib/typert.host.js +7 -4
- package/package.json +12 -2
package/README.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# @dennisrongo/dsh-todo
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@dennisrongo/dsh-todo)
|
|
4
|
+
|
|
5
|
+
**npm:** [`@dennisrongo/dsh-todo`](https://www.npmjs.com/package/@dennisrongo/dsh-todo) ·
|
|
6
|
+
**source:** [dennisrongo/dsh-plugins](https://github.com/dennisrongo/dsh-plugins/tree/main/plugins/dsh-todo)
|
|
7
|
+
|
|
3
8
|
A todo list for the [DeepSeek Harness](https://github.com/deepseek-ai) (dsh) web UI.
|
|
4
9
|
|
|
5
10
|
Registers into the additive `conversation.view` slot — the conversation view
|
|
@@ -7,22 +12,51 @@ ring — so it appears as its **own tab beside Chat and Trajectory** (`order: 20
|
|
|
7
12
|
after chat at `0` and trajectory at `10`) and fills the session pane when
|
|
8
13
|
selected.
|
|
9
14
|
|
|
15
|
+
A **sprint/roadmap task list**, not just a checklist: each task carries a status,
|
|
16
|
+
a priority, and optional release and sprint labels, so a real backlog can live
|
|
17
|
+
next to the code it describes.
|
|
18
|
+
|
|
10
19
|
## Features
|
|
11
20
|
|
|
12
|
-
- **Persisted on disk by the host** —
|
|
13
|
-
|
|
14
|
-
cleared browser cache, and a different browser entirely
|
|
21
|
+
- **Persisted on disk by the host** — one SQLite database per project at
|
|
22
|
+
`<workspace>/.dsh/todo.db`, not in the browser. It survives a restart, a
|
|
23
|
+
cleared browser cache, and a different browser entirely, and it travels with
|
|
24
|
+
the project.
|
|
15
25
|
- **Per-workspace** — each workspace has its own list, keyed by workspace id.
|
|
16
26
|
- **Safe against races** — every write carries the revision it observed; a
|
|
17
27
|
losing write is refused and the view adopts the authoritative list, so two
|
|
18
28
|
open tabs can never silently clobber each other.
|
|
19
|
-
- **
|
|
20
|
-
|
|
29
|
+
- **Real workflow states** — `backlog · todo · in-progress · blocked · done`,
|
|
30
|
+
changed from a pill on the row. A boolean cannot express the two things a
|
|
31
|
+
standup actually asks about: what is moving, and what is stuck.
|
|
32
|
+
- **Priority** — `P0`–`P3`, shown as a chip. Only P0/P1 are coloured, so the
|
|
33
|
+
list flags what is urgent instead of turning into a rainbow.
|
|
34
|
+
- **Release and sprint** — two independent labels: what ships together
|
|
35
|
+
(`v1.2.0`) and when it is worked (`Sprint 24`). A task can be in both.
|
|
36
|
+
- **Group by** — None · Status · Release · Sprint · Priority, with collapsible
|
|
37
|
+
section headers carrying their own `done/total` and progress bar. Grouping by
|
|
38
|
+
status gives you a kanban board without drag-and-drop.
|
|
39
|
+
- **Task detail modal** — click a task's title to open the full dialog: a
|
|
40
|
+
roomy description box plus status, priority, release, sprint and due date.
|
|
41
|
+
Esc or the backdrop closes it, focus is trapped inside and returned to the row
|
|
42
|
+
you came from, and edits commit on close so a stray click never loses them.
|
|
43
|
+
- **Expandable rows** — the chevron still gives a quick in-row peek without
|
|
44
|
+
leaving the list. Double-click a title to rename it inline. One scannable line
|
|
45
|
+
collapsed is what lets a task carry nine fields without becoming a wall of text.
|
|
46
|
+
- **Due dates** — stored as calendar days, so "due the 14th" reads as the 14th
|
|
47
|
+
in every timezone. Overdue tasks are flagged red on the row, due-today amber,
|
|
48
|
+
and a finished task is never overdue.
|
|
49
|
+
- **Own tab** — full-pane view with a progress header and `done/total` score,
|
|
50
|
+
plus live in-progress and blocked counts.
|
|
51
|
+
- **Filter ring** — All · Open · In Progress · Blocked · Backlog · Done ·
|
|
52
|
+
Archive, with live counts; empty states are hidden rather than shown at zero.
|
|
21
53
|
- **Archive, not delete** — check items off, then "Archive completed" files them
|
|
22
54
|
away. Archived items leave every active view but stay in the record, and can be
|
|
23
55
|
restored (↩) or permanently deleted from the Archive view.
|
|
24
|
-
- **Full editing** — add, check off, click-to-edit
|
|
25
|
-
delete.
|
|
56
|
+
- **Full editing** — add, check off, click-to-edit the title, edit the
|
|
57
|
+
description and labels, reorder (▲/▼), archive, and delete. Release and sprint
|
|
58
|
+
inputs suggest labels already in use, so free text converges on a shared
|
|
59
|
+
vocabulary without a releases table to administer.
|
|
26
60
|
- **Themed** — colors come only from the shell's `--dsw-*` tokens, so it follows
|
|
27
61
|
light/dark automatically. Respects `prefers-reduced-motion`.
|
|
28
62
|
|
|
@@ -32,7 +66,7 @@ This is a **dual-face plugin**. Both halves ship from one package.
|
|
|
32
66
|
|
|
33
67
|
| Half | File | Role |
|
|
34
68
|
| --- | --- | --- |
|
|
35
|
-
| Host | `src/index.ts` | `TodoService`, a `TypertRemoteService` that owns the
|
|
69
|
+
| Host | `src/index.ts` | `TodoService`, a `TypertRemoteService` that owns the per-workspace SQLite database and exports `list` / `replace` as `@Remote` methods. |
|
|
36
70
|
| Client | `src/client.tsx` | The React tab. Mounts the host contract and calls it as `ctx.remote.dshTodo.*`. |
|
|
37
71
|
| Bridge | `src/remote.ts` | The Typert Remote descriptor the client mounts. |
|
|
38
72
|
| Shared | `src/types.ts` | Dependency-free vocabulary used by both halves. |
|
|
@@ -68,14 +102,16 @@ pnpm install
|
|
|
68
102
|
pnpm run build # node build/build.mjs — emits lib/index.js + lib/client.js
|
|
69
103
|
pnpm run typecheck # tsc --noEmit
|
|
70
104
|
pnpm test # node test/smoke.mjs — offline, exercises the BUILT lib/ output
|
|
105
|
+
pnpm run test:icons # headless Chrome: icon sizes and the 40px row budget
|
|
106
|
+
pnpm run test:modal # headless Chrome: the dialog escapes the list's scroll container
|
|
71
107
|
```
|
|
72
108
|
|
|
73
109
|
`pnpm test` asserts against `lib/`, so **build before testing**.
|
|
74
110
|
|
|
75
111
|
## Install into a dsh profile
|
|
76
112
|
|
|
77
|
-
The profile must already compose the
|
|
78
|
-
|
|
113
|
+
The profile must already compose the workspace registry, which `@deepseek-ai/dsh-web-app`
|
|
114
|
+
does by default.
|
|
79
115
|
|
|
80
116
|
```bash
|
|
81
117
|
cd ~/.dsh/profiles/<profile>
|
|
@@ -108,7 +144,7 @@ one source of truth and no way to store an archived item without a date.
|
|
|
108
144
|
| Archive completed | footer, any active view | Stamps every done item. Recoverable. |
|
|
109
145
|
| Archive (⌸) | row hover, completed items | Stamps one item. Recoverable. |
|
|
110
146
|
| Restore (↩) | row hover, Archive view | Clears the stamp, returning it to the list. |
|
|
111
|
-
| Delete (✕) | row hover | Removes one item outright. |
|
|
147
|
+
| Delete (✕) | row hover | Removes one item outright, after a confirmation dialog naming the task. |
|
|
112
148
|
| Delete archived | footer, Archive view | Permanently drops every archived item — the only destructive bulk action, and the only one that asks for confirmation. |
|
|
113
149
|
|
|
114
150
|
Archived items are excluded from the progress bar and the done/total score, so
|
|
@@ -120,19 +156,219 @@ swallow a move.
|
|
|
120
156
|
`clearCompleted` (hard delete of done items) is still exported for callers that
|
|
121
157
|
want it, but it is no longer wired to a button.
|
|
122
158
|
|
|
159
|
+
## CLI — for you and for AI agents
|
|
160
|
+
|
|
161
|
+
The same list is reachable from a terminal, so an agent can shell out and manage your tasks
|
|
162
|
+
(and so can you). It targets a workspace **directory** and needs no profile, no session and
|
|
163
|
+
no running server — it works offline and in CI.
|
|
164
|
+
|
|
165
|
+
### Getting the command
|
|
166
|
+
|
|
167
|
+
The plugin ships a `dsh-todo` binary. If the package is already installed into a dsh
|
|
168
|
+
profile, that profile's `node_modules/.bin` has it; otherwise reach it directly:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
npx @dennisrongo/dsh-todo list # no install
|
|
172
|
+
pnpm add -g @dennisrongo/dsh-todo # then just: dsh-todo list
|
|
173
|
+
node /path/to/plugins/dsh-todo/lib/bin.js list # from a checkout
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Requires **Node 22+** — storage is `node:sqlite`, which is built in (and still prints an
|
|
177
|
+
experimental-feature warning on stderr; it is harmless, and `2>/dev/null` silences it).
|
|
178
|
+
|
|
179
|
+
### Everyday use
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
cd ~/projects/my-app # the workspace IS the current directory
|
|
183
|
+
|
|
184
|
+
dsh-todo add "Fix token refresh" --priority p0 --release v1.2.0 --due 2026-03-14
|
|
185
|
+
dsh-todo add "Write migration guide" --sprint "Sprint 24"
|
|
186
|
+
dsh-todo list
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
```text
|
|
190
|
+
[ ] tmtcfbutukp4j todo p0 Fix token refresh (release=v1.2.0 due=2026-03-14)
|
|
191
|
+
[ ] tmtcfbuxr5y9x todo p2 Write migration guide (sprint=Sprint 24)
|
|
192
|
+
[ ] tmtcfbv071e6w in-progress p2 Ship it
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
One task per line: checkbox, id, status, priority, title, then any labels in parentheses.
|
|
196
|
+
That is the whole display contract — it stays greppable and diffable.
|
|
197
|
+
|
|
198
|
+
Move work along, then file it away:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
dsh-todo update tmtcfbut --status in-progress --sprint "Sprint 24"
|
|
202
|
+
dsh-todo show tmtcfbut # everything about one task
|
|
203
|
+
dsh-todo done tmtcfbut
|
|
204
|
+
dsh-todo archive # archive EVERY completed task
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Work on a project you are not `cd`'d into with `--workspace`:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
dsh-todo --workspace ~/projects/other list --open
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Commands
|
|
214
|
+
|
|
215
|
+
| Command | Does |
|
|
216
|
+
| --- | --- |
|
|
217
|
+
| `list` | Show tasks — active only unless `--archived` |
|
|
218
|
+
| `add <title>` | Create a task; every field flag is accepted |
|
|
219
|
+
| `update <id>` | Change one or more fields; needs at least one |
|
|
220
|
+
| `done` / `reopen` | Flip completion, stamping or clearing `completedAt` to match |
|
|
221
|
+
| `rm <id>` | Delete outright — no confirmation, no archive |
|
|
222
|
+
| `archive [<id>]` | Archive one task, or every completed one when no id is given |
|
|
223
|
+
| `show <id>` | Print one task in full, including description and timestamps |
|
|
224
|
+
| `help` | The same reference, in the terminal |
|
|
225
|
+
|
|
226
|
+
### Options
|
|
227
|
+
|
|
228
|
+
| Option | Applies to | Notes |
|
|
229
|
+
| --- | --- | --- |
|
|
230
|
+
| `--workspace <dir>` | all | Workspace directory. Defaults to cwd, and is used **as given** — no upward search for a `.dsh`, so an agent in a subdirectory targets the project it was pointed at. |
|
|
231
|
+
| `--json` | all | Machine-readable output, **including on errors** |
|
|
232
|
+
| `--status <s>` | add, update, list | `backlog` · `todo` · `in-progress` · `blocked` · `done` |
|
|
233
|
+
| `--priority <p>` | add, update, list | `p0`–`p3` (default `p2`) |
|
|
234
|
+
| `--release <label>` | add, update, list | e.g. `v1.2.0` |
|
|
235
|
+
| `--sprint <label>` | add, update, list | e.g. `"Sprint 24"` |
|
|
236
|
+
| `--due <YYYY-MM-DD>` | add, update | A calendar day; impossible dates are refused |
|
|
237
|
+
| `--description <text>` | add, update | Body text — acceptance criteria, repro steps |
|
|
238
|
+
| `--title <text>` | update | Rename |
|
|
239
|
+
| `--open` | list | Everything unfinished, whatever stage |
|
|
240
|
+
| `--archived` | list | Show archived tasks *instead of* active ones |
|
|
241
|
+
|
|
242
|
+
Filters combine, so `list --open --priority p0 --release v1.2.0` is an AND across all three.
|
|
243
|
+
`--key value` and `--key=value` are both accepted.
|
|
244
|
+
|
|
245
|
+
### Driving it from a script or an agent
|
|
246
|
+
|
|
247
|
+
**`--json` is the one to use from a script.** It prints structured output on the error path
|
|
248
|
+
too, so a caller never has to parse a human sentence to find out what went wrong:
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
dsh-todo list --open --json
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
```json
|
|
255
|
+
{
|
|
256
|
+
"count": 3,
|
|
257
|
+
"items": [
|
|
258
|
+
{
|
|
259
|
+
"id": "tmtcfbutukp4j",
|
|
260
|
+
"title": "Fix token refresh",
|
|
261
|
+
"status": "todo",
|
|
262
|
+
"priority": "p0",
|
|
263
|
+
"release": "v1.2.0",
|
|
264
|
+
"dueDate": "2026-03-14",
|
|
265
|
+
"createdAt": 1787889639378
|
|
266
|
+
}
|
|
267
|
+
]
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
`list` returns `{ count, items }`; `add` / `update` / `done` / `reopen` / `rm` return
|
|
272
|
+
`{ item, revision }`; `archive` returns `{ archived, revision }`; `show` returns the task
|
|
273
|
+
itself. A failure returns `{ error, code }` and exits with that code:
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
$ dsh-todo update nope --status done --json; echo "exit=$?"
|
|
277
|
+
{
|
|
278
|
+
"error": "no task matching \"nope\"",
|
|
279
|
+
"code": 3
|
|
280
|
+
}
|
|
281
|
+
exit=3
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Exit codes are distinct so a script can branch on **why** a command failed:
|
|
285
|
+
|
|
286
|
+
| Code | Means |
|
|
287
|
+
| --- | --- |
|
|
288
|
+
| `0` | Success |
|
|
289
|
+
| `2` | Usage — unknown command, bad flag, malformed value |
|
|
290
|
+
| `3` | Not found — a well-formed request that matched no task |
|
|
291
|
+
|
|
292
|
+
Piping into `jq` covers most agent work:
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
# ids of everything blocked
|
|
296
|
+
dsh-todo list --status blocked --json | jq -r '.items[].id'
|
|
297
|
+
|
|
298
|
+
# fail CI if any P0 is still open
|
|
299
|
+
test "$(dsh-todo list --open --priority p0 --json | jq '.count')" -eq 0
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Ids accept any **unambiguous prefix** — `dsh-todo done tmtcfbut`. Ids are time-ordered so
|
|
303
|
+
short prefixes collide; an ambiguous one is an **error listing the candidates** rather than a
|
|
304
|
+
guess at which task you meant.
|
|
305
|
+
|
|
306
|
+
An **empty value clears a field**: `--release ""` removes the release. From a shell there is
|
|
307
|
+
no other way to say "unset this" as opposed to "leave it alone".
|
|
308
|
+
|
|
309
|
+
> **PowerShell drops empty arguments.** `--release ""` arrives at Node as a bare `--release`
|
|
310
|
+
> flag, so the field is left untouched instead of cleared. Use the `--release=` form there,
|
|
311
|
+
> which survives intact on every shell.
|
|
312
|
+
|
|
313
|
+
Invalid values are **refused, never dropped** — `--due 2026-02-31` exits `2` instead of
|
|
314
|
+
quietly storing nothing, because an agent would otherwise never learn its date was ignored.
|
|
315
|
+
|
|
316
|
+
### Is it safe alongside the running app?
|
|
317
|
+
|
|
318
|
+
Yes, and it is tested. SQLite is a multi-process database: the file lock refuses a writer that
|
|
319
|
+
lands inside another process's transaction rather than letting it interleave, and the CLI sets
|
|
320
|
+
a `busy_timeout` so it waits for the harness to commit instead of failing. Verified live — the
|
|
321
|
+
CLI wrote while a running server held its handle, and the API returned the new task with no
|
|
322
|
+
restart.
|
|
323
|
+
|
|
324
|
+
The one visible effect is that an **already-open browser tab** may need a refresh: every write
|
|
325
|
+
bumps a revision token, so the tab's next write is refused and it adopts the authoritative
|
|
326
|
+
list. That is the designed reconciliation, not lost data.
|
|
327
|
+
|
|
328
|
+
## The task model
|
|
329
|
+
|
|
330
|
+
```ts
|
|
331
|
+
{ id, title, description?, status, priority, release?, sprint?, dueDate?,
|
|
332
|
+
createdAt, completedAt?, archivedAt? }
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
| Field | Notes |
|
|
336
|
+
| --- | --- |
|
|
337
|
+
| `title` | Short and scannable — the one line a collapsed row shows. Capped at 500. |
|
|
338
|
+
| `description` | The body: acceptance criteria, repro steps, links. Its own 5000 cap, because reusing the title's 500 would silently truncate real notes. |
|
|
339
|
+
| `status` | `backlog \| todo \| in-progress \| blocked \| done`. **The source of truth** — there is no separate `done` flag to fall out of sync. |
|
|
340
|
+
| `priority` | `p0`–`p3`, default `p2` so an unranked task sits mid-pile rather than jumping the queue. |
|
|
341
|
+
| `release` | What ships together, e.g. `v1.2.0`. |
|
|
342
|
+
| `sprint` | When it is worked, e.g. `Sprint 24`. |
|
|
343
|
+
| `dueDate` | `YYYY-MM-DD`. A calendar day, not an instant — an epoch would bind it to a timezone and let one task read as two different days. Impossible dates like `2025-02-31` are rejected rather than rolled forward. |
|
|
344
|
+
|
|
345
|
+
**Release and sprint are separate on purpose.** A task can be worked in Sprint 24
|
|
346
|
+
and ship in v1.3.0; collapsing them into one field loses the ability to answer
|
|
347
|
+
either question. Both are free text rather than entities — grouping and
|
|
348
|
+
filtering work with no releases table, no CRUD, and no migration to rename one.
|
|
349
|
+
|
|
350
|
+
`completedAt` is written only by the status transition, so it can never claim a
|
|
351
|
+
task is finished that isn't. Absent optional fields are absent *keys*, never
|
|
352
|
+
`''`, so "no release" has exactly one representation.
|
|
353
|
+
|
|
123
354
|
## Storage
|
|
124
355
|
|
|
125
356
|
| Location | Contents |
|
|
126
357
|
| --- | --- |
|
|
127
|
-
|
|
|
358
|
+
| `<workspace>/.dsh/todo.db` | SQLite: one `todo` row per task, plus a `meta` table holding `revision` and `updatedAt`. |
|
|
128
359
|
|
|
129
|
-
|
|
130
|
-
items live in the same `items` array — archiving never moves data between
|
|
360
|
+
Archived items live in the same table — archiving never moves data between
|
|
131
361
|
collections, so nothing can be lost in a partial write.
|
|
132
362
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
363
|
+
### Upgrading from the checklist version
|
|
364
|
+
|
|
365
|
+
Existing databases are migrated **in place** on first open: the new columns are
|
|
366
|
+
added with `ALTER TABLE`, `title` is backfilled from the old `text`, and
|
|
367
|
+
`done = 1` becomes `status = 'done'`. Nothing is dropped, and a list written by
|
|
368
|
+
the old version keeps its order, its completion stamps and its archive.
|
|
369
|
+
|
|
370
|
+
The old `text`/`done` columns are still written alongside their replacements, so
|
|
371
|
+
downgrading to the previous version still reads a sane list.
|
|
136
372
|
|
|
137
373
|
### Migration from the old browser-only version
|
|
138
374
|
|