Oc Todo
Per-session todo lists for OpenCode V2 - a `todo` tool with real plugin storage and a read-only sidebar checklist, including V1 todowrite parity.
0
0
20.0
Multi-signal model
5 hours ago
2026-10-05
Install and configure
opencode.jsonWrites to this project's opencode.json — applies to this repository only.
opencode.json
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["oc-todo@0.2.3"]
}Writes to ~/.config/opencode/opencode.json — applies to every project.
~/.config/opencode/opencode.json
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["oc-todo@0.2.3"]
}If you want to modify the plugin locally, install it into the project and reference the local path.
shell
pnpm add -D oc-todoOpenCode loads npm dependencies through its embedded runtime on startup and caches them locally — no manual global install needed.
✅ oc-todo
Per-session todo lists for your OpenCode terminal. Real plugin storage — not a decorative widget.
🚀 Quick start
Add the plugin to your opencode.jsonc:
// opencode.jsonc
{ "plugins": ["oc-todo"] }
Restart OpenCode. That's the whole setup — no config file, no options. The
todo tool is available to the agent immediately, and the checklist appears in
the sidebar beside an open session whenever that session has todos.
🧰 The todo tool
The list is real, per-session state: every mutation is written to plugin storage under a session-scoped key, and every read returns exactly what is stored. Nothing here is ephemeral UI text.
| Action | Arguments | What it does |
|---|---|---|
list |
— | Read back the stored list for this session |
write |
todos[] |
V1 todowrite parity — replace the whole list |
add |
content [status] [priority] |
Append one item |
update |
id [content] [status] [priority] |
Change one item by id (exact or unambiguous prefix) |
complete |
id |
Mark one item completed |
clear |
— | Wipe the list |
status: pending · in_progress · completed · cancelled
priority: high · medium · low
A write item may carry an id; when it does, the existing item keeps its
identity and createdAt, so a rewrite is a true edit rather than a new list.
Blank items are dropped and unknown status/priority fall back to
pending/medium.
📋 The sidebar checklist
tui.tsx renders the stored list read-only in the sidebar.content slot. It
refreshes on rpc.todo.changed and falls back to a slow poll, so it also works
against a remote server whose events this TUI is not subscribed to.
Rendering rules — decided, not configurable:
- No todos → nothing renders.
- Any pending / in_progress → the full checklist; closed lines are muted.
- All completed / cancelled → collapses to one muted line,
✓ Todos n/n, so finished work leaves closure without leaving a stale list. - Click the header to toggle (▾/▸) — a manual choice wins for that session. The removed V1 sidebar had the same toggle; this one works at any list length, not only above two items.
Storage never auto-prunes. Items change only when the caller mutates them
(write, clear, or a per-item action). The render rules are what keep the
surface quiet — the data keeps the history.
💾 Storage
Plugin storage under todos/session/<sessionID>, durable in the host's SQLite
store. The list is keyed by session id, so it is scoped to the calling session
and survives across turns and server restarts.
{ "version": 1, "todos": [
{ "id": "m1a2b", "content": "write docs", "status": "pending",
"priority": "medium", "createdAt": 0, "updatedAt": 0 }
] }
🧩 Layout
A local directory plugin is resolved by filename at the plugin root — the
package.json exports map is only consulted for installed (named) packages —
so the entries sit at the root:
index.ts server plugin: the `todo` tool + the RPC implementation
tui.tsx CLI plugin: sidebar checklist renderer
contract.ts shared RPC contract (plain JSON Schema, no bare imports)
- As a package (
"plugins": ["oc-todo"]):exports["."]→index.ts,exports["./tui"]→tui.tsx. - As a local directory (
"plugins": ["./plugins/oc-todo"]): the same files at the directory root.
⚠️ V2 note
Both entrypoints are deliberately dependency-free at load time: index.ts
exports a plain { id, setup } definition and the shared contract is a plain
object. That is always valid — Plugin.define and Rpc.define are identity
helpers — and it also keeps the plugin loadable where the bare
@opencode/plugin specifier is not resolvable from a local directory plugin
(observed on the stock 2.0.22 binary, 2026-10). The TUI entry keeps
@opencode/plugin/tui, which the TUI runtime injects.
License
MIT © 2026 nathwn12
Similar plugins
Aside
opencode-aside
Claude Code's /btw side chat for opencode - ask a one-shot question in an overlay, with no tools and nothing added to the transcript
Session Elapsed
opencode-session-elapsed
OpenCode TUI plugin that shows session elapsed, cumulative work, and response timers in the prompt line.
Todolist
opencode-todolist
OpenCode V2 plugin restoring session todo list tools (todowrite/todoread) with a sidebar todo strip