@charlesmatte/todos
Shared todo, task and reminder store that any of your Kody-connected agents can use.
ProductivityTopics: todo, task, reminder, agents, productivity
- Version
- 0.2.0
- License
- Published
- October 14, 2026
- Pinned commit
53689ea- Rating
- No ratings yet
- Forks
- 0
- Adaptation effort
- —
README
Agent docstodos — a shared todo / task / reminder store for your AI agents
Intent
You talk to several AI agents (a coding agent, a chat assistant, an email agent…). Each one hears "remind me to…" or "add a task for…" but keeps nothing between conversations, and none of them sees what the others noted. This package gives all of them one durable, shared list inside Kody. Any agent connected to your Kody account can create, list, update, complete and delete items, produce a daily digest, and remind you when things are due. Every change records which agent made it.
Success: you can throw a todo at any agent, ask any other agent "what's on my plate?", and get the same answer.
No UI. It is a set of package exports agents call from Kody execute.
Quick start
Fork / install the package from the Kody community page into your own account. Your fork has its own private storage; nothing is shared with the original author. The examples below use the original scope
@charlesmatte/todos; replace it with your scope after forking (for examplekody:@yourname/todos/create).Set your time zone once (default is
UTC):import settings from 'kody:@charlesmatte/todos/settings' export default async () => settings({ timeZone: 'America/New_York' })First calls:
import createTodo from 'kody:@charlesmatte/todos/create' import listTodos from 'kody:@charlesmatte/todos/list' import digest from 'kody:@charlesmatte/todos/digest' export default async () => { const { todo } = await createTodo({ title: 'Renew domain name', due: '2026-11-01', priority: 'high', tags: ['admin'], by: 'my-assistant' }) const { text } = await listTodos({}) // open items, sorted const daily = await digest({}) // overdue / today / next 7 days / undated return { id: todo.id, text, digest: daily.text } }
Export reference
All exports take one plain object. Unknown keys are rejected with an error listing the accepted
shape. null values count as "not provided". Every write returns the stored record and a one-line line
summary, e.g. T12 [high] Renew domain name — due 2026-11-01 (project: Website) #admin.
| Export | Input | Returns |
|---|---|---|
. (overview) | — | export list, current time zone, rules, docs pointer |
./settings | { timeZone? } | { timeZone, configured, default, changed } |
./create | { title, by, notes?, due?, priority?, tags?, kind?, project?, client?, timeZone? } | { todo, line, timeZone } |
./list | { status?, overdue?, today?, dueBefore?, dueAfter?, undated?, tag?, project?, client?, kind?, priority?, q?, includeDeleted?, limit?, timeZone? } | { today, timeZone, total, count, todos, text } |
./get | { id, timeZone? } | { todo, overdue, line } (todo includes history) |
./update | { id, by, title?, notes?, due?, clearDue?, snooze?, priority?, kind?, tags?, addTags?, removeTags?, project?, client?, timeZone? } | { todo, line, changes } |
./complete | { id, by, note? } | { todo, line } |
./reopen | { id, by } | { todo, line } |
./delete | { id, by, purge? } | { id, deleted, purged } |
./digest | { days?, includeUndated?, tag?, project?, client?, timeZone? } | { today, timeZone, counts, sections, text } |
Field details:
title(≤300 chars),notes(≤5000),project/client(≤120): free text.priority:"low" | "normal" | "high"(defaultnormal).kind:"todo" | "task"(defaulttodo), a label only.tags: array of strings. Stored lowercase, a leading#is stripped, duplicates removed.id:"T12"or"12".list.status:"open"(default) |"done"|"all".dueBefore/dueAfterare inclusive; date-only bounds cover the whole day.qmatches every word against title, notes, tags, project, client, id.limit1–500 (default 100).update.snooze: exactly one of{ days: 1-365 }(shifts from the current due date, or from today if overdue/undated, keeping the time of day),{ hours: 1-720 },{ until: "2026-10-20" } (any due format). Use only one ofdue,clearDue,snooze. To clear text fields pass""(notes,project,client).tagsreplaces;addTags/removeTagsedit.digest.days: 1–60 (default 7).
Examples
import updateTodo from 'kody:@charlesmatte/todos/update'
import completeTodo from 'kody:@charlesmatte/todos/complete'
import reopenTodo from 'kody:@charlesmatte/todos/reopen'
import deleteTodo from 'kody:@charlesmatte/todos/delete'
import getTodo from 'kody:@charlesmatte/todos/get'
import listTodos from 'kody:@charlesmatte/todos/list'
await listTodos({ overdue: true })
await listTodos({ dueBefore: '2026-10-20', tag: 'admin', priority: 'high' })
await listTodos({ q: 'invoice', status: 'all' })
await updateTodo({ id: 'T12', by: 'my-assistant', snooze: { days: 2 } })
await updateTodo({ id: 'T12', by: 'my-assistant', due: '2026-10-20T14:00', addTags: ['urgent'] })
await completeTodo({ id: 'T12', by: 'my-assistant', note: 'paid online' })
await reopenTodo({ id: 'T12', by: 'my-assistant' })
await deleteTodo({ id: 'T12', by: 'my-assistant' }) // soft delete
const { todo } = await getTodo({ id: 'T12' }) // includes todo.historyDue date formats
| Input | Meaning |
|---|---|
"2026-10-20" | all-day item, due by the end of that day in the store time zone |
"2026-10-20T14:00" | 14:00 wall-clock time in the store time zone |
"2026-10-20T18:00:00Z", "2026-10-20T14:00-04:00" | exact instant (shown in the store time zone) |
"today", "tomorrow" | all-day, relative to the store time zone |
Natural language such as "next Friday" is rejected. The calling agent should convert it first. Each stored
due keeps { date, time, at, timeZone }: at is the UTC instant for timed items, null for all-day ones.
Time zone setup
- The store time zone is a per-store setting (
./settings),UTCuntil you set it. Set it once to your IANA zone (America/Toronto,Europe/Paris, …). Invalid names are rejected. - It governs: parsing date-only and wall-clock dues,
today/tomorrow, "overdue" for all-day items,list.today, and digest buckets. - Any time-aware export also accepts a per-call
timeZoneoverride. - Changing the setting later does not rewrite existing items: timed items keep their exact instant, all-day items keep their calendar date.
The by convention
Every write requires by: a short, stable name for the agent (or person) making the change, e.g.
"claude-code", "email-agent", "me". It is saved as createdBy / updatedBy and in each history
entry ({ at, by, action, changes }, last 50 kept), so you can see who added or changed what.
Delete vs purge
./delete is a soft delete. The item is archived, hidden from list and digest, visible with
list({ includeDeleted: true }), and restorable with ./reopen. purge: true removes it permanently
and cannot be undone. Agents should purge only when you explicitly ask.
Concurrency
Storage is this package's durable SQLite (packageStorage()), shared by every caller of your copy.
Ids come from an atomic counter (T1, T2, …; ids are never reused). Updates use optimistic versioning:
an edit applies only if the item did not change since it was read, otherwise it re-reads and retries up to
5 times. Two agents editing the same item at once therefore never silently overwrite each other.
Recipes
Daily digest job
Create a Kody job (search Kody for "schedule a job") that runs each morning with this module, and send
text wherever you like (email, chat):
import digest from 'kody:@charlesmatte/todos/digest'
export default async () => {
const { text, counts } = await digest({ days: 7 })
if (counts.overdue + counts.today + counts.upcoming === 0) return { skipped: true }
return { text } // hand this to your email / chat integration
}Due reminders
Run every 15–30 minutes and notify about items that just became due:
import listTodos from 'kody:@charlesmatte/todos/list'
export default async () => {
const soon = new Date(Date.now() + 30 * 60_000).toISOString()
const { todos } = await listTodos({ dueBefore: soon, dueAfter: new Date().toISOString() })
return todos.filter((t) => t.due?.at).map((t) => t.line) // timed items due in the next 30 min
}Agent prompt snippet
Paste into your agents' instructions or memory:
Store all my todos, tasks and reminders in the Kody package
todos(kody:@YOUR_SCOPE/todos/...). When I say "remind me", "add a todo/task", or "note that I need to…", call./createwith a clear title, a due date inYYYY-MM-DDorYYYY-MM-DDTHH:MM(my time zone) if I gave one, andby: "YOUR_AGENT_NAME". When I ask what's on my plate, call./digestor./listand show thetext. Use./complete,./update(includingsnooze) and./deletewith the item id (T12). Never usepurgeunless I ask.
Limitations
- Single list per installed copy: no multi-user sharing, assignees, or per-agent permissions. Any agent with access to your Kody account can change any item.
- No recurring items, subtasks or attachments. History keeps the last 50 entries per item.
- No built-in notifications: pair it with a Kody job (see recipes) to send digests and reminders.
- Filtering happens in memory, which is fine for thousands of items but not designed for very large stores.
- Due dates must be explicit; natural-language dates are not parsed.
Report this listing
Log in to report this listing.