@dennisrongo/dsh-todo 0.1.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 +147 -0
- package/cordis.patch.yml +9 -0
- package/lib/client.js +208 -0
- package/lib/index.js +353 -0
- package/lib/typert.host.js +138 -0
- package/package.json +75 -0
package/README.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# @dennisrongo/dsh-todo
|
|
2
|
+
|
|
3
|
+
A todo list for the [DeepSeek Harness](https://github.com/deepseek-ai) (dsh) web UI.
|
|
4
|
+
|
|
5
|
+
Registers into the additive `conversation.view` slot — the conversation view
|
|
6
|
+
ring — so it appears as its **own tab beside Chat and Trajectory** (`order: 20`,
|
|
7
|
+
after chat at `0` and trajectory at `10`) and fills the session pane when
|
|
8
|
+
selected.
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- **Persisted on disk by the host** — the list lives in a dsh storage domain at
|
|
13
|
+
`~/.dsh/storages/dsh_todo.json`, not in the browser. It survives a restart, a
|
|
14
|
+
cleared browser cache, and a different browser entirely.
|
|
15
|
+
- **Per-workspace** — each workspace has its own list, keyed by workspace id.
|
|
16
|
+
- **Safe against races** — every write carries the revision it observed; a
|
|
17
|
+
losing write is refused and the view adopts the authoritative list, so two
|
|
18
|
+
open tabs can never silently clobber each other.
|
|
19
|
+
- **Own tab** — full-pane view with a progress header and `done/total` score.
|
|
20
|
+
- **Filter ring** — All · Open · Done · Archive, with live counts.
|
|
21
|
+
- **Archive, not delete** — check items off, then "Archive completed" files them
|
|
22
|
+
away. Archived items leave every active view but stay in the record, and can be
|
|
23
|
+
restored (↩) or permanently deleted from the Archive view.
|
|
24
|
+
- **Full editing** — add, check off, click-to-edit, reorder (▲/▼), archive, and
|
|
25
|
+
delete.
|
|
26
|
+
- **Themed** — colors come only from the shell's `--dsw-*` tokens, so it follows
|
|
27
|
+
light/dark automatically. Respects `prefers-reduced-motion`.
|
|
28
|
+
|
|
29
|
+
## Architecture
|
|
30
|
+
|
|
31
|
+
This is a **dual-face plugin**. Both halves ship from one package.
|
|
32
|
+
|
|
33
|
+
| Half | File | Role |
|
|
34
|
+
| --- | --- | --- |
|
|
35
|
+
| Host | `src/index.ts` | `TodoService`, a `TypertRemoteService` that owns the storage domain and exports `list` / `replace` as `@Remote` methods. |
|
|
36
|
+
| Client | `src/client.tsx` | The React tab. Mounts the host contract and calls it as `ctx.remote.dshTodo.*`. |
|
|
37
|
+
| Bridge | `src/remote.ts` | The Typert Remote descriptor the client mounts. |
|
|
38
|
+
| Shared | `src/types.ts` | Dependency-free vocabulary used by both halves. |
|
|
39
|
+
|
|
40
|
+
The browser holds no authority over the data: it renders an optimistic echo and
|
|
41
|
+
the committed host revision always wins.
|
|
42
|
+
|
|
43
|
+
### Two build constraints that are easy to break
|
|
44
|
+
|
|
45
|
+
Both are asserted by the smoke test, because both fail silently at runtime:
|
|
46
|
+
|
|
47
|
+
1. **The host half must not be minified.** The Typert gateway discovers a
|
|
48
|
+
`@Remote` method's wire fields by reading its *parameter names* out of
|
|
49
|
+
`Function.prototype.toString()`. Minifying `request` to `e` changes the wire
|
|
50
|
+
contract.
|
|
51
|
+
2. **The host half must target `es2021`.** `@Remote` is a TC39 *standard*
|
|
52
|
+
decorator, and Node 22 cannot yet parse native decorator syntax. esbuild only
|
|
53
|
+
downlevels decorators when the target predates them; at `es2022+` it emits
|
|
54
|
+
them verbatim and the host half fails to load.
|
|
55
|
+
|
|
56
|
+
### Peer dependencies, not dependencies
|
|
57
|
+
|
|
58
|
+
`@deepseek-ai/cordis`, `dsh-typert-protocol`, and `dsh-storage-domain` are
|
|
59
|
+
declared as **peer** dependencies and marked external in the host build. They
|
|
60
|
+
must resolve to the *running dsh install's* copies — a second cordis instance
|
|
61
|
+
would register into a different registry and the service would never appear.
|
|
62
|
+
dsh profiles set `autoInstallPeers: false`, so this resolves correctly.
|
|
63
|
+
|
|
64
|
+
## Commands
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pnpm install
|
|
68
|
+
pnpm run build # node build/build.mjs — emits lib/index.js + lib/client.js
|
|
69
|
+
pnpm run typecheck # tsc --noEmit
|
|
70
|
+
pnpm test # node test/smoke.mjs — offline, exercises the BUILT lib/ output
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`pnpm test` asserts against `lib/`, so **build before testing**.
|
|
74
|
+
|
|
75
|
+
## Install into a dsh profile
|
|
76
|
+
|
|
77
|
+
The profile must already compose the storage rows (`storage`, `storage-json`,
|
|
78
|
+
`storage-domain`); `@deepseek-ai/dsh-web-app` does this by default.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
cd ~/.dsh/profiles/<profile>
|
|
82
|
+
pnpm add "file:/absolute/path/to/dsh-plugins/plugins/dsh-todo"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
then add the row to `cordis.patch.yml`:
|
|
86
|
+
|
|
87
|
+
```yaml
|
|
88
|
+
- insert:
|
|
89
|
+
- id: dsh-todo
|
|
90
|
+
name: '@dennisrongo/dsh-todo'
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
and restart the profile. The single row mounts both halves: the host service
|
|
94
|
+
and the browser tab.
|
|
95
|
+
|
|
96
|
+
> The profile installs `file:` dependencies as a **copy**, not a symlink, so
|
|
97
|
+
> after `pnpm run build` you must re-run `pnpm install` in the profile to pick
|
|
98
|
+
> up the new artifacts.
|
|
99
|
+
|
|
100
|
+
## Archiving
|
|
101
|
+
|
|
102
|
+
Completed work is **archived, not deleted**. An item carries an optional
|
|
103
|
+
`archivedAt` epoch-ms stamp; its *presence* is the archived state, so there is
|
|
104
|
+
one source of truth and no way to store an archived item without a date.
|
|
105
|
+
|
|
106
|
+
| Action | Where | Effect |
|
|
107
|
+
| --- | --- | --- |
|
|
108
|
+
| Archive completed | footer, any active view | Stamps every done item. Recoverable. |
|
|
109
|
+
| Archive (⌸) | row hover, completed items | Stamps one item. Recoverable. |
|
|
110
|
+
| Restore (↩) | row hover, Archive view | Clears the stamp, returning it to the list. |
|
|
111
|
+
| Delete (✕) | row hover | Removes one item outright. |
|
|
112
|
+
| Delete archived | footer, Archive view | Permanently drops every archived item — the only destructive bulk action, and the only one that asks for confirmation. |
|
|
113
|
+
|
|
114
|
+
Archived items are excluded from the progress bar and the done/total score, so
|
|
115
|
+
tidying up never makes progress appear to regress. They sort newest-archived
|
|
116
|
+
first, so the Archive view reads as a log. Reordering is computed in
|
|
117
|
+
active-list space, so a hidden archived entry between two visible rows cannot
|
|
118
|
+
swallow a move.
|
|
119
|
+
|
|
120
|
+
`clearCompleted` (hard delete of done items) is still exported for callers that
|
|
121
|
+
want it, but it is no longer wired to a button.
|
|
122
|
+
|
|
123
|
+
## Storage
|
|
124
|
+
|
|
125
|
+
| Location | Contents |
|
|
126
|
+
| --- | --- |
|
|
127
|
+
| `~/.dsh/storages/dsh_todo.json` | `tables.workspaces[<workspaceId>]` → `{ items, revision, updatedAt }` |
|
|
128
|
+
|
|
129
|
+
Each item is `{ id, text, done, createdAt, completedAt?, archivedAt? }`. Archived
|
|
130
|
+
items live in the same `items` array — archiving never moves data between
|
|
131
|
+
collections, so nothing can be lost in a partial write.
|
|
132
|
+
|
|
133
|
+
The file is plain JSON and safe to read. Editing it by hand while dsh is running
|
|
134
|
+
is not recommended — the host holds the authoritative copy in memory and will
|
|
135
|
+
overwrite the file on its next write.
|
|
136
|
+
|
|
137
|
+
### Migration from the old browser-only version
|
|
138
|
+
|
|
139
|
+
Earlier versions stored todos in `localStorage` under `dsh-todo:items`. On first
|
|
140
|
+
run, that list is imported **once** into the first workspace that opens with an
|
|
141
|
+
empty stored list, and the import is then marked with `dsh-todo:migrated`. The
|
|
142
|
+
original key is deliberately left in place rather than deleted.
|
|
143
|
+
|
|
144
|
+
## Notes
|
|
145
|
+
|
|
146
|
+
- The host half publishes a service, so it belongs to the profile's host
|
|
147
|
+
composition — not to an agent preset.
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# dsh bundle patch: inserts this plugin into a profile's layer stack, so
|
|
2
|
+
# `dsh plugin add` mounts it without hand-editing the profile's own
|
|
3
|
+
# cordis.patch.yml. Both id: and name: are required — a bare id: is an
|
|
4
|
+
# id-targeted override of an existing row and silently no-ops.
|
|
5
|
+
#
|
|
6
|
+
# One row mounts BOTH halves: the host service (dshTodo) and the browser tab.
|
|
7
|
+
- insert:
|
|
8
|
+
- id: dsh-todo
|
|
9
|
+
name: '@dennisrongo/dsh-todo'
|