@ferrousapps/listr
v0.1.7
Published
Run Claude Code on your own machine against tasks you assign in Listr. Connect with a pairing code, allow-list the repos it may touch, and it implements tasks and opens PRs for you to review.
Maintainers
Readme
@ferrousapps/listr
Run Claude Code on your own machine against tasks you assign in Listr. Hand a task to Claude from the app; the agent on your machine implements it on a branch, opens a pull request, runs a review pass, and reports status back to the task. You merge.
You, anywhere ───(Listr / Firestore)───▶ your machine: listr-agent start
"Hand off to Claude" on a task claim → branch
(repo comes from the list) → claude (code) → gh pr create
→ claude (review) → PR comment
◀── status: Queued · Coding · PR Open · Reviewed- Pairing-code auth — no passwords. Connect with a one-time code from the app.
- Repo allowlist is the hard security boundary — the agent only ever touches repos you added on this machine.
- Shared pools — everyone on a collaborative list can connect machines, and a teammate can hand a task to any online machine in the pool.
- The agent never merges. Every change lands as a PR for you to review.
The command this package installs is listr-agent.
Requirements
- Node ≥ 20
- Claude Code CLI (
claude), signed in (claudeonce interactively, or an API key configured). - For GitHub repos: the GitHub CLI (
gh), authenticated with push + PR rights:gh auth login. - For Bitbucket repos: an app password (
BITBUCKET_USERNAME+BITBUCKET_APP_PASSWORD) or an access token (BITBUCKET_TOKEN) — Bitbucket has nogh-style CLI, so the agent uses git-over-HTTPS + the REST API. - A Listr Pro/Elite account (Coding Agents is a paid capability).
Install
npm install -g @ferrousapps/listrThis puts the listr-agent command on your PATH. (Prefer not to install
globally? Use npx @ferrousapps/listr <command> anywhere below.)
Update later with npm update -g @ferrousapps/listr.
Quick start
The fastest path is the guided wizard — it asks for each value (Enter skips any, add it later), then offers to allow-list a repo and pair the machine:
listr-agent setupIt writes your answers to ~/.listr-agent/.env; re-run it any time to change or
fill in skipped values. Prefer to do it by hand? The individual steps:
# 1. Pair this machine. Get the code from Listr → Agents → "Connect a machine".
listr-agent connect ABCD-1234
# 2. Allow-list the repo(s) this machine may touch (the security boundary).
listr-agent repos add --slug your-org/your-app
# …or an existing local checkout:
listr-agent repos add --path ~/code/your-app --alias app
# 3. Sanity-check everything (tools, sign-in, visible tasks).
listr-agent doctor
# 4. Run it.
listr-agent startThen in Listr:
- Open a list, edit its details, and set the GitHub repository
(
owner/repo) — every task you hand off inherits it. - On any task, choose Handoff to Claude → pick a machine (or Any available agent) → Hand off.
The task advances Queued → Claimed → Coding → Committing → PR Open → Reviewing →
Reviewed, with the PR link attached. Manage running jobs (cancel / rerun /
remove, and view logs) from the Agents tab.
You stay signed in across restarts — you only
connectonce per machine.
Per-task options: tag a task Urgent to have agents pick it up first (it
even preempts a running non-urgent job). Add a custom field named "Branch" to
name the working branch the agent creates; otherwise it auto-generates
listr-agent/<id>-<slug>.
Commands
listr-agent start Watch for tasks and run them (default)
listr-agent once Process whatever's eligible now, then exit
listr-agent doctor Check config, tools, sign-in, and visible work
listr-agent connect <code> Pair this machine with a code from the app
listr-agent disconnect Sign out and remove this machine's session
listr-agent mcp Run as an MCP server so Claude Code can create tasks
listr-agent repos list Show this machine's repo allowlist
listr-agent repos add --slug owner/repo [--alias name]
listr-agent repos add --path /abs/path [--alias name]
listr-agent repos remove <slug|path|alias>
listr-agent machines List machines registered to your accountAdd --verbose (or -v) to any command for detailed logging.
listr-agent doctor is the safe pre-flight: it lists exactly which tasks it can
see and whether each one's repo is allow-listed.
How repos work
A task's repo is untrusted cloud input — the agent never clones or runs it directly. Instead:
- You set a repository on the Listr list (or override per task) — GitHub or Bitbucket.
- The agent resolves that value against this machine's local allowlist
(
listr-agent repos add). If it isn't allow-listed, the task is skipped on this machine — another machine that has allow-listed it can run it.
So the cloud can only ever pick from the repos you explicitly approved on a given machine. Provider is inferred from the form:
listr-agent repos add --slug owner/repo # GitHub
listr-agent repos add --slug bitbucket.org/workspace/repo # Bitbucket
listr-agent repos add --slug workspace/repo --provider bitbucket
listr-agent repos add --path ~/code/your-app --alias app # local checkoutOn the Listr list, write owner/repo for GitHub or bitbucket.org/workspace/repo
for Bitbucket. GitHub clones via gh; Bitbucket clones via git-over-HTTPS using
your configured credentials and opens PRs through the Bitbucket REST API.
Sources & the shared pool
Control which tasks a machine watches with LISTR_AGENT_SOURCE:
| Source | Watches | Default |
|---|---|---|
| own | your personal lists only | ✅ |
| collab | collab lists you own or are shared on | |
| both | personal + collab | |
collab/both run task text authored by collaborators, so they require an
explicit opt-in: set LISTR_AGENT_ALLOW_COLLAB=1.
When you run with collab/both, your machine joins that list's shared pool:
every collaborator can see it online and hand it a task. If several machines
watch the same list, an atomic claim guarantees only one runs a given task,
and you can target a specific machine when handing off.
# Join collab pools (in addition to your own tasks):
LISTR_AGENT_SOURCE=both LISTR_AGENT_ALLOW_COLLAB=1 listr-agent startCreate tasks from Claude Code (MCP)
The same pairing also lets Claude Code (or any MCP client) create tasks in your Listr projects — the reverse direction from handing tasks off. Because the session acts as you, it writes your own projects directly; no bot account, no extra login.
# 1. Pair once (if you haven't already):
listr-agent connect ABCD-1234 # code from Listr → Agents → "Connect a machine"
# 2. Register the MCP server with Claude Code:
claude mcp add listr -- npx -y @ferrousapps/listr mcpClaude Code then has these tools:
| Tool | What it does |
|---|---|
| list_projects | Lists your projects (personal + shared) with their ids. Hidden/PIN lists are excluded. |
| list_tasks | Lists tasks in a project; pass a parent task to see its sub-tasks. |
| create_task | Creates a task. Supports nesting (subtasks for a whole tree, or parentTaskId to add under an existing task), a description, due date, number/currency, assignees (shared lists only), and attachments (local files or links). |
| update_task | Updates an existing task by id: mark it complete/incomplete (tick its checkbox), rename it, or edit its description, due date or number. |
| list_context | Reads a task's working-context notes (the task-detail Context tab) — load this before working a task. |
| add_context | Appends a context note to a task (findings, decisions, repo layout). Tagged as written by the agent. |
| update_context | Edits an existing context note by its entry id. |
| delete_context | Removes a context note by its entry id. |
Examples of what you can ask Claude Code:
- "Add a task 'Ship v2 release' to my Roadmap project with sub-tasks for QA, docs, and changelog."
- "Create a bug ticket under the 'Auth' task and attach ./crash.log."
- "Read the context on the 'Auth' task, then note what you changed when you're done."
Identify a project by title (case-insensitive) or id — run list_projects
first if a title is ambiguous. Attachments are capped at 10 MB and reject
executables, matching the app. Files on a shared list can only be uploaded by
that list's owner; collaborators can still attach links.
The MCP server speaks JSON-RPC over stdio (status goes to stderr). It's a local process Claude Code starts on demand — nothing is exposed to the network.
Configuration
Everything has a sensible default. To override, set environment variables or copy
.env.example to .env next to the package (or in the
directory you run from). Common knobs:
| Variable | Default | Purpose |
|---|---|---|
| LISTR_AGENT_AUTH | pairing | pairing (code) or password (bot user) |
| LISTR_AGENT_SOURCE | own | own · collab · both |
| LISTR_AGENT_ALLOW_COLLAB | 0 | required 1 for collab/both |
| LISTR_AGENT_NAME | hostname | display name for this machine |
| LISTR_AGENT_HOME | ~/.listr-agent | config/state directory |
| CLAUDE_MODEL | opus | e.g. claude-opus-4-8 |
| CLAUDE_PERMISSION_MODE | acceptEdits | Claude permission mode |
| CLAUDE_ALLOWED_TOOLS | Bash Edit Write … | tools Claude may use |
| CLAUDE_DANGEROUS | 0 | 1 → run Claude with --dangerously-skip-permissions |
| CLAUDE_TIMEOUT_MS | 1800000 | per-run timeout (30 min) |
| WORKDIR | ~/.listr-agent/repos | where remote repos are cloned |
| BITBUCKET_USERNAME + BITBUCKET_APP_PASSWORD | — | Bitbucket app-password auth |
| BITBUCKET_TOKEN | — | Bitbucket access token (alternative to app password) |
The Listr project's public Firebase web config is baked in, so you don't
need to supply it. Forking Listr? Point the agent elsewhere with FIREBASE_*
and LISTR_PAIRING_ENDPOINT — see .env.example.
State on disk
All under ~/.listr-agent (override with LISTR_AGENT_HOME):
machine-id— stable id for this machineauth/— the persisted Firebase session (refresh token; mode 0600)credentials.json— connection marker (email/uid; mode 0600)repos.json— your repo allowlistrepos/— cached GitHub clones
listr-agent disconnect removes the session and signs this machine out.
Security model (please read)
- Repo allowlist is the boundary — untrusted cloud repo values are only ever resolved against what you added locally; everything else is refused.
- Own tasks by default. Running collaborators' tasks (
collab/both) is a deliberate opt-in (LISTR_AGENT_ALLOW_COLLAB=1). - Per-machine session.
disconnectcuts a machine off. - No auto-merge. Output is always a PR you review.
CLAUDE_DANGEROUS=1removes Claude's permission prompts for fully unattended runs — only use it on repos/machines where that's acceptable.
Troubleshooting
gh not authenticated→gh auth login(needs push/PR rights on the repo).- Task is skipped: "not in this machine's repo allowlist" →
listr-agent repos add --slug owner/repoon the machine that should run it. - Nothing happens after handoff → run
listr-agent doctorto confirm the machine is signed in, the source includes the task's list, and its repo is allow-listed. Add--verbosetostartto watch each step live. Saved session expired→listr-agent connect <code>once more.- Claude seems stuck → it streams progress; a long pause usually means a slow
step, not a hang. Raise
CLAUDE_TIMEOUT_MSfor big tasks.
Part of Listr. Source:
ferrousdesigner/listr-ce under
tools/listr-agent.
