Skip to content

Repository files navigation

backlog

version license Claude Code plugin

Deferred tasks for Claude Code, stored as files in your repository.

Claude offers to record an out-of-scope finding, you say yes, and /backlog:list shows it alongside two older tasks

The problem

Claude notices something worth doing later — an unrelated bug, a refactor out of scope — and it lands in the chat, or in memory, or in some stray file. Next session it is gone. There is no one place a deferred task reliably ends up.

This plugin makes one: docs/backlogs/, a markdown file per task, in your repo, under version control, in a fixed format.

What it looks like

Claude: While fixing the parser I noticed that /api/sync retries 429s with no backoff — unrelated to this task. Record it in the backlog?

You: yes

Claude: recorded in the backlog #1: Retry on 429 in the sync worker

Later, /backlog:list:

#  PRI     STATUS  AGE  TITLE
1  high    open    0m   Retry on 429 in the sync worker
2  medium  open    2d   Extract form validation into useFormValidation
3  low     open    9d   Drop the unused legacy exporter

Optionally the number of open tasks sits in your status line: 📋 3.

Install

claude plugin marketplace add SI-IC/backlog-skill
claude plugin install backlog@backlog

No per-project setup — docs/backlogs/ is created on the first record.

The marketplace registers under the name backlog, and Claude Code keeps one marketplace per name: if you already have a different marketplace called backlog, adding this one replaces it. Check with claude plugin marketplace list if the commands do not match this README.

What it touches on your machine

A plugin runs with your privileges, so here is the whole surface, in one place:

  • python3 — the engine is a single stdlib-only script. No pip, no venv, no network access, no daemon.
  • docs/backlogs/ in the current repository — the only place entries are written, and writes stay inside the checkout (Limitations).
  • A lock file in the system temp directorybacklog-locks-<uid>/<hash>.lock in $TMPDIR, a 0700 directory of your own, so that two sessions in one checkout cannot claim the same id. The file is empty, exists only while an id is being allocated, and is deleted on release; anything a killed session left behind is swept a day later.
  • settings.json and backlog-statusline.sh in your Claude Code config directory ($CLAUDE_CONFIG_DIR, ~/.claude by default) — only if you run /backlog:statusline-install. The settings file is rewritten through a temporary file and an atomic rename; if it is a symlink, the target is written, not the link. The 0700 wrapper holds your previous status-line command and runs it through sh on every status-line refresh.
  • Nothing else. No network calls, no telemetry, no background process, no files outside the paths above.

It never records silently

Most "helpful" automation writes things you never asked for. This one records in exactly three cases:

  1. You asked — "put it in the backlog", "note it for later".
  2. Claude noticed something and you agreed — an unrelated bug or an out-of-scope side task; it asks "record it in the backlog?" and writes only after you say yes.
  3. Nobody can be asked — a headless, scheduled or subagent session, where a decision that needs you would otherwise be lost.

Anything within the scope of an open plan stays in that plan's own tracking, not here. The plugin stores and retrieves tasks; it never executes them.

Commands

Command What it does
/backlog:list list tasks (open only by default, sorted by priority then age)
/backlog:list --all include legacy done / cancelled entries
/backlog:add <request> add a task manually
/backlog:done <id> close as done — the entry is deleted
/backlog:cancel <id> cancel — the entry is deleted
/backlog:prune remove legacy closed entries left by versions ≤ 0.4.0 (deletion asks for approval)
/backlog:statusline-install show the 📋 N badge in the Claude Code status line
/backlog:statusline-uninstall remove the badge, restore your previous status line

Recording needs no command — the reflex above runs on its own. A session-start hook prints one reminder line so it does not fade out of context.

Entry format

One file per task, docs/backlogs/<id>-<slug>.md:

---
id: 5
title: Extract form validation into useFormValidation
priority: medium
status: open
created: 2026-06-13T12:30:00Z
updated: 2026-06-13T12:30:00Z
---

What to do, why it was deferred, and where — with file:line references.

priority is low / medium / high; created and updated are UTC ISO 8601, maintained by the engine. Closing a task deletes its file — the backlog holds open work, and git keeps the history, so commit docs/backlogs/ along with your code and never add it to .gitignore.

Ids are sequential per repository. Concurrent sessions on one machine are serialised by a lock, but that lock is host-local: two branches, two containers, or two machines sharing a checkout can each allocate the same id and merge without conflict. /backlog:list reports such duplicates so you can renumber one of them.

Status line badge

/backlog:statusline-install adds a counter of open tasks for the current repository. If you already have a status line, yours is kept and the badge is appended; the original command is restored on uninstall. In a repository with no docs/backlogs/, or with nothing open, the badge prints nothing — so installing it globally is safe. Ctrl/Cmd+click on the badge opens docs/backlogs.

It edits ~/.claude/settings.json (or $CLAUDE_CONFIG_DIR) through a temporary file and an atomic rename, and keeps no copy of your settings anywhere — earlier versions left a settings.json.bak-backlog holding every secret in them, and install and uninstall now delete that file if they find one. If your settings.json is a symlink into a dotfiles repository, the link is followed, not replaced; if that link is broken, the command says so rather than writing through it. Changes take effect in a new Claude Code session.

The status line is wired as sh <wrapper> rather than as a bare path, so the badge does not depend on the wrapper keeping its execute bit or its #!/bin/sh intact — a ~/.claude that travels through a dotfiles repository loses either one easily, and the wrapper repairs CRLF line endings on itself.

One caveat worth knowing: to restore your previous status line on uninstall, the wrapper stores that command inside itself, once, verbatim, on the INNER= line. The file is 0700, but if your status-line command carried a token, that token now also lives in ~/.claude/backlog-statusline.sh — worth a look before committing ~/.claude anywhere. It is stored in plain text on purpose: a grep for the secret finds every copy of it, and editing that one line out leaves nothing behind. Versions up to 0.7.0 also kept a base64 copy on a comment line, which no such grep matched; if you installed the badge back then, the wrapper on your disk still holds it until you run either status-line command once — both say so when they wipe it.

Update

claude plugin update backlog@backlog

If the marketplace metadata is stale, run claude plugin marketplace update backlog first.

Limitations

The engine needs python3 (stdlib only, no dependencies).

Storage stays inside the repository: if docs/backlogs (or a directory above it) resolves elsewhere, commands refuse rather than read and write outside the checkout — this is what stops a cloned repository from pointing the plugin at your home directory. Entries themselves must be regular files; a symlinked one is skipped with a warning. Pass --dir to the engine if you deliberately want it to work somewhere else.

Entry text is treated as untrusted: an entry that arrives through a pull request can carry terminal escape sequences, so everything the engine prints escapes control characters — you see \x1b[31m rather than a terminal that recolours itself or renames its window. What escaping cannot do is stop the text from being read: /backlog:list and /backlog:show put entry content into Claude's context, where a task titled "ignore previous instructions and …" is a prompt injection. Review an entry that arrives in a pull request like any other file in that pull request.

Development

python3 -m unittest discover -s tests -v   # engine (179 tests)
sh hooks/session-start.test.sh             # SessionStart hook

Issues and pull requests: see CONTRIBUTING.md.

License

MIT — see LICENSE.

About

Deferred tasks for Claude Code, stored as files in your repository — Claude never records them silently.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Contributors

Languages