I maintain a fleet of small OSS projects, and the cross-cutting "what do I do next" list used to live in my head, then in a SaaS board, then nowhere useful. What finally stuck is embarrassingly low-tech: a single todo.txt in todo.txt format, plus about 1,200 lines of dependency-free shell and Python around it — a small bash CLI, a curses TUI, and two report generators, zero pip installs.
The wall that killed the SaaS board for me: the board lived behind an API, and my AI coding agents live in the repo. The surface we share now is the file itself — both of us read and write the same todo.txt.
The twist is that I'm not the only one reading and writing that file. My AI coding agents read it, grep it, and append to it too. Same file, same format, no API in between. This post is about why a plain-text board turned out to be the right shared surface for a human-plus-agents workflow, and what the moving pieces actually are.
What you'll learn:
- Why a plain-text
todo.txtis a better shared surface for human-plus-agent work than a hosted board - The three moving pieces: the file itself, a nightly cron rollup, and a dependency-free TUI
- How "just say it and it's on the board" falls out of everyone sharing one file — no API, no lock
The pieces
There are only three things, and the file is the important one.
1. todo.txt — the source of truth
Every open task is one line. Priority in parentheses, a creation date, the description, a +project tag, an @context tag, and an optional due: in ISO format:
(A) 2026-01-10 add CSV export to the reports page +myapp @dev due:2026-01-15
(B) 2026-01-09 write onboarding docs +docs-site @writing
2026-01-08 investigate flaky auth test +myapp @dev due:2026-01-20
That's the whole schema. (A)–(C) is priority (A first), +project names the repo, @context is the kind of work (@dev, @review, @ops, @writing, ...), and due:YYYY-MM-DD is the deadline. Because dates are ISO, plain string comparison sorts them correctly — the sort itself needs no date parsing at all (relative dates like due:+3d are resolved to an absolute ISO date once, at write time).
Completed tasks don't clutter the board. A done command moves the line, prefixed with x and a completion date, into a separate done.txt archive.
A tiny CLI (todo.sh — pure bash over coreutils; the gantt and issue views shell out to a small dependency-free Python script) is the front door:
todo.sh # today: overdue + due-today + priority-A
todo.sh week # deadlines within 7 days
todo.sh all # everything, sorted by priority then due date
todo.sh ls +myapp # filter by project or any substring
todo.sh add "(A) add CSV export +myapp @dev due:+3d"
todo.sh done "CSV export"
Two conveniences that pull their weight: add accepts relative due dates — due:today, due:tomorrow, due:+3d, due:+2w, due:fri — and rewrites them to an absolute ISO date before saving, so the stored file stays unambiguous. And add stamps the creation date for you. The query and display path is mostly grep, awk, and sort over one text file.
2. A nightly cron rollup
A single crontab line runs a rollup command once a night (22:00, in my case):
0 22 * * * /path/to/todo.sh rollup >> rollup.log 2>&1
rollup reads the board and writes a dated Markdown digest — daily/2026-01-12.md — with counts (open / overdue / due-today / this-week) and each bucket listed out. It's idempotent: run it again and it just regenerates the same day's file, so if the machine was asleep at 22:00 I can regenerate the current day by hand (or my agent does it at the start of a session). No state, no database — the digest is a pure function of todo.txt.
The rollup also appends an ASCII Gantt chart. A separate gantt command renders the board's start:→due: ranges two ways: ASCII in the terminal, and a Mermaid diagram written to gantt.md that renders on GitHub, in Obsidian, or on mermaid.live. The Gantt is a view, never a second copy of the data — tasks without a due: simply don't appear.
3. A dependency-free TUI
For "leave it open and add/complete on the spot," there's a full-screen curses TUI (todo-tui.py). It imports nothing but the Python standard library — curses, os, re, sys, unicodedata, datetime. No pip install, ever.
It shows todo.txt live and, crucially, watches the file's mtime: if todo.sh, cron, an agent, or a phone app rewrites the file, the TUI reloads automatically. a adds, d/Enter completes, e edits a line in place, / filters, and Tab cycles between the list, the Gantt view, and an issues view. I keep it pinned in a dedicated tmux pane.
Because it's the same todo.txt underneath, nothing about the TUI is privileged. It's just one more editor of the file.
Why plain text — agents can read it
Here's the part that made this click for a human-plus-AI setup.
An AI coding agent already has three verbs that work perfectly on a text file: Read, Edit, and Grep. A todo.txt needs no adapter, no MCP server, no OAuth dance, no rate limit. The agent opens the file the same way it opens any source file in the repo, and it understands the format after seeing it once because the format is self-evident — it's just lines.
Compare that to a SaaS board. To let an agent touch a hosted kanban you need an integration, credentials, and a network round-trip — and interactive-auth integrations are exactly the thing that tends to be missing in headless contexts: a cron job, a scheduled agent, a CI run. A plain file in the repo is always reachable, from every one of those contexts, with zero setup.
Plain text buys you three more things for free:
-
git tracks it. Every add and every completion is a diff — at whatever granularity you commit it (mine goes in with the surrounding workspace commits, so it's per-commit history, not per-edit). "What was I doing that week" is just
git logon one file. -
it's greppable. "What's open for
+myapp?" isgrep +myapp todo.txt, for me and for the agent identically. - it's portable. todo.txt is a real format with real mobile apps. Point one at the same file — through whatever file sync you already run (Dropbox, Syncthing) — and you've got a phone view without writing anything.
The format being boring is the feature. There's no layer where the human's mental model and the agent's mental model can drift apart, because there's no layer at all — just the file.
"Just say it and it's on the board"
The day-to-day loop is the payoff. In the middle of a coding session I say something like "add a task to ship the CSV export by Friday," and the agent runs:
todo.sh add "(A) ship CSV export +myapp @dev due:fri"
The relative due:fri becomes a concrete date, the line lands in todo.txt, and the TUI in my other pane redraws a second later because its mtime watch fired. When the work is done, "mark the CSV export done" becomes todo.sh done "CSV export" and the line moves to the archive. Nothing I say has to be structured; the agent knows the schema and fills it in.
There's no lock and no coordination protocol. In theory an append and a filter-and-rewrite could still race and lose a line; in practice it hasn't bitten, because writes are single quick operations on one small file, the collision window is milliseconds wide, and if a lost update ever does happen, the line is one git log -p away. It's a calculated bet on low write concurrency, not a guarantee — and the TUI treats the file as the truth rather than caching its own copy. "Just say it and it's on the board" isn't a feature anyone built; it falls out of everyone sharing the same plain-text file.
Takeaway
When your collaborators are AI agents, the interface you share with them matters more than the features of any one tool. A hosted board optimizes for a human clicking a UI. A todo.txt optimizes for anything that can read a file — you, your shell, a cron line, a phone app, and an LLM agent — participating on equal footing with no integration in between.
Pick the substrate your agents can already touch. For a task list, that substrate is a plain-text file, and the whole rest of the system is about 1,200 lines of dependency-free glue — a bash CLI, a curses TUI, and two report generators, nothing you couldn't rebuild from this post in a weekend.
This is part of a series on human-AI collaboration patterns. Two companion pieces — one on the collaboration "shape" I use with coding agents, and one on a "don't decide too early / keep options live" discipline — are still in the drafts and will link here once published.
Primary sources
The board conventions and the CLI/TUI behavior described here are taken from the actual tooling (a README.md spec, a small bash CLI, and a dependency-free curses TUI). The tool and file names are simplified for this post (todo.sh, todo-tui.py); the real tooling has different names but the same shape and behavior. The board lives in a private workspace repo — that's why there is no link — and the spec above is deliberately complete enough to rebuild it. All task examples in this post are fictional; the real board's contents are omitted.
What's the lowest-tech surface you've ended up sharing with an AI agent — and did it beat the "proper" tool it replaced? I'd genuinely like to hear about it in the comments.
── Hideyuki Mori (Ayane International) 🔗 hideyuki-mori.com
Top comments (0)