If you have tried to give an AI coding agent access to your task board, you have probably hit the wall I did: the board lives behind an API, and the agent lives in your repo. Here is the low-tech surface I use instead, where both of us read and write the same file.
I maintain a fleet of small OSS projects. 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 a few dozen lines of dependency-free shell and Python around it.
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:
todo.txt
is a better shared surface for human-plus-agent work than a hosted boardThere are only three things, and the file is the important one.
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.
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.
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.
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 log
on one file.+myapp
?" is grep +myapp todo.txt
, for me and for the agent identically.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.
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, and yet the human, the CLI, the cron job, and the TUI never fight, because there's exactly one writer pattern — append a line, or filter-and-rewrite — over one small file, 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.
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 a few dozen lines of dependency-free glue.
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.
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). All task examples in this post are fictional; the real board's contents are omitted.
── I run a suite of self-hosted business tools in production. I take on consulting for legacy modernization and shared-hosting constraints. Contact: github.com/hideyukiMORI