Feature request: external/programmatic writes to the session task list (~/.claude/tasks) — or a disk re-read contract for the task panel

Status Open
Reported on v2.1.236
Maintainer reply None cached
Activity 0 comments · opened Aug 20, 2026

Feature request

A sanctioned way for external processes (CLI tools the model invokes, hook scripts) to write the session's native task list — the store behind TaskCreate/TaskUpdate, persisted at ~/.claude/tasks/<session-id>/*.json and rendered by the terminal task panel. Either a documented external-writer contract (with the UI re-reading from disk), or an explicit API/hook/control-protocol message for task injection.

Use case

We run a repo-local task/plan CLI (phases → tasks → mechanical "done" gates) that the model drives via Bash. Today the model must mirror every CLI mutation into the native panel by hand with TaskCreate/TaskUpdate calls — pure ceremony that models forget at some rate, leaving the panel stale for the human watching it. The obvious fix is for the CLI to write the panel files directly: the CLI already knows every task and status transition, and the model's only remaining job would be nothing at all.

We built exactly that and measured why it can't work today (Claude Code ~2.1.236, Linux):

What we measured

  1. The on-disk rows are readable and well-formed for external writers. Externally-written <n>.json rows are picked up by TaskList, by the periodic task reminders injected into context, and (after ingestion) by TaskUpdate — including respecting .highwatermark for id minting.
  2. The task panel UI never repaints from disk. External file writes, TaskList, TaskGet, and SIGWINCH all leave the UI untouched; only a mutating task-tool call repaints (a bare no-field TaskUpdate suffices, and it does re-read the whole directory).
  3. The store is memory-authoritative and garbage-collects divergence. Once the in-process store is non-empty, a task-tool call that observes the directory diverging from memory (a row it didn't author, or a row modified under it) takes a proper-lockfile-style lock (.lock.lock directory) and deletes every row in the directory — observed repeatedly under an inotify watch, including reads (TaskList) as the trigger. An external writer therefore oscillates with the harness: it writes rows, the next tool call sweeps them, the next CLI mutation re-mints them under fresh ids.

So external writing is unsupported by design today, and there is no path around it from outside the process.

What would solve it (any one of these)

  • A documented contract that the task store treats the on-disk directory as authoritative (or merges instead of sweeping), plus a UI repaint on external change (a file watcher, or re-read on panel open); or
  • a hook event / control-protocol message / CLI subcommand that lets non-model code create/update/delete tasks in the live session; or
  • at minimum, a documented "re-adopt from disk" affordance so a cooperating external writer plus one cheap model-side call can stay consistent.

We have a working external writer (id minting against .highwatermark, status mapping, foreign-row protection, atomic writes) sitting behind an env flag, ready to arm the day any of the above exists. Happy to share details or test a beta.

View original on GitHub ↗