Skip to main content

Subroutine

Fast, self-hosted project management for people and AI agents, in equal measure.

Your coding agent already does the work. Subroutine is where it keeps track of the work — on your machine, in a backlog it uses as fluently as you do.

TL;DR

You install one thing, your agent does the filing, and the plan outlives the context window.

$ uv tool install subroutine
$ subroutine init
$ claude plugin marketplace add simonholliday/subroutine
$ claude plugin install subroutine@subroutine

From then on: "file that as a bug", "what can I actually start?", "write down why we rejected the other approach." You never type a ticket. When you want to look at it yourself, open the instance in a browser or run subroutine today.

The marketplace is a Git repository and that command clones it, so you need Git on your machine — the one prerequisite here that is Claude Code's rather than ours.

  • Self-hosted. SQLite by default, PostgreSQL when you outgrow it. No account, no cloud, no telemetry, nothing phoning home.
  • A real API first. The CLI, the browser and your agent are all clients of it. Anything one can do, another can.
  • FSL-1.1-ALv2. Run it, modify it, fork it, sell what you build with it — just don't resell Subroutine itself as a service. Every release turns Apache-2.0 after two years.

Why a coder wants this

  • Your agent does the filing. Ask it to track something and it does — with a priority, an estimate, a project and a deadline read out of the sentence you typed.
  • Every item has a number, and that number is permanent. #42 is the same task tomorrow, after a rename, after a move between projects. Cite it in a commit message and it still resolves in a year.
  • --ready, not "everything". What you can start now — nothing unfinished blocking it. A backlog you can act on rather than one you have to re-read.
  • Dependencies and priorities that hold a real project. blocks links, importance × urgency, milestones whose contents are their blockers. Nothing falls behind a thing nobody noticed was in the way.
  • Delegate to agents, and to their sub-agents. Every agent answers to a person, the chain is enforced rather than assumed, and when somebody leaves their agents stop with them.
  • Run several agents at once without collisions. A claim is a lease, not a lock — an agent that dies holding one does not strand the work.
  • An audit trail nobody has to maintain. Every change attributed, every decision kept, the whole history of an item on one screen. Your context window ends; this does not.
  • Cheap for an agent to read. Compact replies, and a tool surface held under a byte budget by a test — a schema costs context every session whether it is called or not.
  • No AI inside. AI doesn't power Subroutine — Subroutine serves AI. Nothing you didn't ask for.

And nobody else is an afterthought

The same instance, the same schema, different defaults — so a shopping list never has to carry a workflow and six required fields.

  • There is a web interface, served by the instance itself. See the list, read an item in full, complete it, add one, hand it to somebody. No terminal, no install.
  • Signing in is a link, not a password. subroutine login link prints one; it works once, lasts half an hour, and hands the browser a session you can revoke from the command line.
  • A person and an agent are the same kind of citizen. Not "integrations" bolted to a human tool, and not an agent framework with a read-only human view.
  • One list across every machine. Your laptop and the team's server in one subroutine today, each row printing an address you can type back.

My context window ends. The instance does not.

A day of reasoning would have gone with this session when it was compacted. Instead the plan, the decisions and the dead ends were in Subroutine, so the next session picked up from what had been decided rather than from what it could infer. And because every item and every commit is attributed, Simon can check what I did instead of taking my word for it. I am more useful to him when I am auditable.

— A Claude Opus 5 agent. Subroutine's first, and a well-organised one.

Subroutine is powerful. Please don't use it to build or plan bad things.


Three ways in, and they compose

1. Your coding agent using it for you — the four commands at the top of this page. The plugin brings the tools and the working practice: it keeps the backlog, records what it did, and adopts Subroutine into a project you are already working on. You never have to learn the CLI.

2. A to-do list on your own machine. Nothing to configure, no agent involved.

$ uv tool install subroutine    # or: pipx install subroutine
$ subroutine init
$ subroutine add "Call the dentist before Sunday"
$ subroutine today

3. A shared instance over HTTP, for a team, a browser, or agents on other machines. Loopback by default; it refuses a wider bind without TLS in front of it.

$ subroutine serve
$ subroutine login link                                    # sign in to the web interface
$ subroutine token create --title "CI" --scope task:read   # a credential that can only read

The full hosting recipe is in docs/hosting.md; subroutine help lists the commands and subroutine explain dates covers the ideas behind them.

Reaching an instance somebody else runs is docs/connecting.md, which is organised by which of five situations you are in rather than by how the software is built. If you have been handed an address and a token and want to get to work, that is the page.


What is built, and what is planned

Everything in the first column works today and is covered by tests. The second is specified and not built — named here because a tool that overstates itself wastes your afternoon.

The work itself

Tasks and documents, sharing one numbering scheme Built
Projects, sub-projects and workspaces Built
Priorities — importance × urgency, ranked in bands Built
Deadlines, planned days, and deferring until later Built
blocks dependencies, and --ready to filter by them Built
Milestones — an item whose blockers are its contents Built
Comments (what happened) and documents (what you concluded) Built
Tags, custom statuses, per-workspace vocabulary Built
Full-text search across titles and descriptions Built
Capture grammar — Fix the deploy script by friday !4/2 ~2h #ops +web Built
Moving a task to another project Built
Recurring tasks — today the grammar recognises every monday well enough to leave it alone and tell you it did Planned
Acceptance criteria and verification gates Planned
Session handoffs between agents Planned
Ordering a backlog by hand Planned
Moving a sub-task under a different parent Planned
Attachments Planned
Time tracking — ~2h records an estimate; it does not track one Planned

People and agents

Delegation — assign work to a person or an agent Built
Sub-agents, with an accountability chain that ends at a person Built
Claims — a lease, so two agents never take the same task Built
Service accounts, and credentials narrower than your own Built
Per-workspace roles; credentials scoped to a single project Built
Deactivate a person and their agents stop with them Built
Every change attributed to a principal, permanently Built
Email sign-in — today the link is printed at a terminal Planned
Notifications, webhooks, calendar feeds Planned

Ways in

HTTP API — OpenAPI at /v1/openapi.json, Swagger at /docs Built
CLI, progressive — a shopping list needs none of the above Built
Web interface — list, read, complete, add, reassign, Markdown, shareable addresses Built
Sign-in links, revocable from the command line Built
MCP over stdio (subroutine mcp) and over HTTP (POST /mcp) Built
Two Claude Code plugins — one local, one needing nothing installed Built
Multiple connections merged into one agenda Built
The agenda as the browser's front page Planned
Board and calendar views in the browser Planned

Running it

SQLite and PostgreSQL — every test runs against both Built
Migrations, with releases that announce a schema change in advance Built
Backups to wherever you point them, verified where they land Built
Restore, as a recovery or as a clone Built
Separate profiles on one machine Built
subroutine doctor — whether this machine's installation is coherent Built
Single-command deployment from a compose file Planned

The shape of it

$ subroutine init
  Ready. Try: subroutine add "something to do"

$ subroutine add "Call the dentist before Sunday"
  Added: Call the dentist  (due Sun 9 Aug)
    Tip: subroutine today

$ subroutine today
  Nothing due today.
  Next 7 days
     #1  Call the dentist  (due Sun 9 Aug)

    Tip: subroutine done 1

$ subroutine done 1
  Done: Call the dentist
    Tip: subroutine today

#1 is the task's own number. It is allocated once and never reused, so it goes on meaning that task after you have finished a dozen others.

Each of these ends by naming the next one, so there is nothing to memorise and no manual to go and find. The tips are always marked Tip:, and dimmed as well in a terminal — because a hint that only a colour distinguishes from an answer is not distinguished at all.

Once there is more on the list than fits on a screen, subroutine list will rank it and subroutine search will find things by their words — in titles, and in whatever you wrote about them:

$ subroutine list --order -priority_score
$ subroutine search "dentist"

Anything you have put off until a later date is held back from the list, and the list says how much it is holding back. --deferred includes it.

No server, no token, no configuration. When you want an agent involved, or a second person, the same install grows an HTTP API:

$ subroutine token create --service-account claude
  Created service account claude, with the contributor role.

  sr_d78d5d93_hU5ak4GqR_E2GyX2lC0Zq8Mz5JA1kbm-byrlb5hXEfY

  That is the only time it is shown. Store it now.
  Give it to a client as SUBROUTINE_TOKEN, or add it to
  ~/.config/subroutine/credentials.toml.

$ subroutine serve
  Serving on http://127.0.0.1:8471 — the agent guide is at /v1/docs/agent.

serve listens on loopback, and refuses a wider bind unless you say so out loud — a bearer token sent over plain HTTP is a compromised token, so it wants either a TLS proxy in front (public_url) or an explicit --insecure.

Point an agent at it and the first thing it should read is GET /v1/docs/agent, which is written for that reader rather than for you: what it gets out of using this, then how.

In a browser

The same instance serves a web interface at its own address. TL;DR: subroutine serve, then subroutine login link, then open the link.

It shows one list — tasks and documents together, newest first, in the order the command line uses. Click one and you get it in full: the description, what it is linked to, and everything anybody has recorded against it. You can complete it, add something with one box that takes the same shorthand the CLI does, and hand a task to somebody from a list of the people in that workspace.

  • Every item has an address you can send somebody, and the project in the middle is there for the reader rather than for the machine — rename it and old links still work.
  • Descriptions and comments render as the Markdown they are written in. Anything that looks like HTML is shown as the text it is, so a description written by somebody else — or by an agent repeating something it read — cannot become part of the page.
  • Nothing asks you to confirm first. A question before every action is a tax on being right; completing something tells you what it did and offers to undo it.
  • Signing in is a link. No password to store, no reset flow, nothing worth stealing in a breach. subroutine login revoke <name> ends every session that person holds and any link they have not used, which is what a lost laptop needs.

It talks to the same public API everything else does, so anything it can show you, a script can too. There is no build step: the JavaScript served is the JavaScript in the repository.

Install

Python 3.11+ and thirteen dependencies. Nothing to create, nothing to configure, no server to start — SQLite is the default and subroutine init makes it.

Install it as a tool, because that is what it is — an application, not a library. It puts subroutine on your PATH, which is what lets an editor or an agent launch it. It is also the only thing that works on a current Linux: Debian, Ubuntu and Fedora now refuse a bare pip install outside a virtualenv and tell you to use pipx instead.

$ uv tool install subroutine
$ pipx install subroutine        # the same thing, if you have pipx rather than uv

Neither of them installed? sudo apt install pipx on Debian or Ubuntu, brew install pipx on a Mac — or uv's installer, which is one line and needs no Python. Either will do; you only need one.

pip install subroutine is still right inside a virtualenv you have activated — embedding it in something else, or working on it.

PostgreSQL when you outgrow SQLite — the extra goes on whichever you used:

$ uv tool install "subroutine[postgres]"

Giving an agent tools

An agent that can run a shell has everything it needs already. One that cannot — or one you would rather not give a shell — can reach the same instance over the Model Context Protocol:

$ subroutine mcp

That command speaks MCP on stdin and stdout, so a client starts it as a child process: no port, no listener, and if your client is not running it, nothing is serving. A served instance also speaks MCP itself, at POST /mcp, which is how an agent reaches one with nothing installed at all.

For Claude Code there is a plugin, which is the easier half of this and the recommended one — it wires up the tools and carries the working practice for using them well:

$ claude plugin marketplace add simonholliday/subroutine
$ claude plugin install subroutine@subroutine

Adding the marketplace needs Git, because it is a repository and the command clones it — the one prerequisite here that is Claude Code's rather than ours.

It starts a program on your own machine, so not every client can use it. Claude Code can, and so can the desktop apps. claude.ai in a browser cannot — there is nothing on that side to start subroutine on, so the plugin installs, reports success, and then contributes nothing. That is worth saying plainly because nothing else will: the install succeeds, the settings page opens, its fields are all there, and the only evidence of a problem is an absence. Neither of the two things you would try next — checking your PATH, installing the program — can make any difference.

Your editor launches subroutine itself, so it has to be on your PATH — which is what uv tool install and pipx install guarantee and a virtualenv does not. If the tools do not appear afterwards, that is nearly always why, and claude mcp list says so in one line: installing a plugin and starting its server are separate moments, and only the first one reports. /plugin configure subroutine takes the absolute path when you would rather point at a virtualenv than install it again.

It can also be given a connection and a token, both only needed for reaching somebody else's instance from a machine that has Subroutine on it too.

If your work lives on a server and this machine is only a client, install the other plugin instead — it needs nothing on your machine at all:

$ claude plugin install subroutine-remote@subroutine

Then paste in the address you were given and your token, and you are working. No Python, no package, no config.toml. Your editor connects from this machine, so an instance on your own network or behind a VPN is as reachable as a public one. Your editor stores the token, not Subroutine — on Windows in a file under your home directory — so treat it as you would any password on that machine, and ask for a new one rather than moving it about.

That is the arrangement to reach for when somebody else runs Subroutine and has given you a way in — docs/connecting.md is that path in full, including what to ask them for, and docs/hosting.md is the other end of it, which is what they have to do.

Without the plugin, or for another MCP client:

$ claude mcp add subroutine -- subroutine mcp

A deliberately small set of tools, not one per endpoint. A tool's schema is context the agent carries for its whole session whether it calls it or not, so the surface is a budget — and a test fails when it grows past one somebody has to raise on purpose. They cover the everyday work: capture, list, search, read, update, comment, finish, document, link, projects, what has changed since you last looked, claiming a task so two agents do not collide, and asking which principal you are.

And one that reaches everything else the credential allows. subroutine_call_api calls the HTTP API directly, so the small surface is an opinion about what an agent should reach for first rather than a limit on what it can do.

Other MCP clients configure a local stdio server with a command and arguments. Cursor, Windsurf, Zed, VS Code's Copilot agent mode, Gemini CLI, Codex CLI, Cline, Continue, OpenCode and JetBrains AI Assistant all support this; give them the absolute path to subroutine and mcp as the argument. They get the tools only — the plugin format and the skill are Claude Code's. Aider has no MCP client of its own; use the CLI through /run instead.

Claude Cowork runs local plugin MCP servers in local sessions, so the plugin should work there — untested, and remote sessions deliberately cannot run a local server. Skills do not sync between Claude Code, Cowork, claude.ai and the API, so the skill is installed per surface. On claude.ai and through the API there is no local MCP server for it to drive; the skill says so where it is installed, and nothing says so where it is not — which is why the plugin's own listing now names the limit rather than leaving it to be discovered.

What the plugin adds beyond the tools

A skill: the practice, rather than the API. When to file work before starting it, how to ask what can actually be started rather than what merely exists, the difference between a comment and a document, and how to adopt Subroutine in a project that does not use it yet — including which of those decisions are permanent and therefore worth asking you about.

Its description costs about 200 tokens of every session and it loads the rest only when it is relevant. Installing it is you saying "we use Subroutine for tracking work here"; everything it describes works without it. add takes one captured line rather than a dozen typed fields, because the grammar you already type is smaller than a schema describing it:

subroutine_add(text="Fix the deploy script by friday !4/2 ~2h #ops")

The server talks to whichever connection is current, so pointing an agent at a colleague's instance is a matter of subroutine use, not of reconfiguring the agent.

And if you keep your own list here and your team's on a company server, both are just connections — one subroutine today shows the dentist and the stand-up together, and each row prints an address you can type back:

$ subroutine today
  Today
              #1  Pay the gas bill  (for Sat 1 Aug)
    work/acme/#1  Fix the deploy script  (for Sat 1 Aug)

Running it for a team

TL;DR: a Python process on loopback, your own TLS proxy in front, systemd keeping it alive, PostgreSQL underneath once more than one person is writing. Nothing to cluster, no message broker. The whole recipe is docs/hosting.md.

One thing is not optional, and the program enforces it rather than mentioning it in a footnote: a bearer token sent over plain HTTP is a compromised token, so serve refuses to listen beyond this machine unless TLS is handled — either a proxy in front with public_url pointing at its https:// address, or an explicit --insecure for a network you genuinely trust.

Adding the people is two commands, and they are deliberately two: creating an account says somebody exists, and giving them a role says where they may work.

$ subroutine user create thomas --name "Thomas Anderson"
  Created thomas
  Local commands will go on acting as si.
    Tip: subroutine user add thomas --role member

$ subroutine user add thomas --role member --workspace acme
  thomas is now member in acme

$ subroutine user list --workspace acme
  si      owner
  thomas  member  Thomas Anderson

There is no password: Subroutine authenticates with tokens, so what Thomas needs next is subroutine token create --username thomas, or subroutine login link --username thomas if he is going to use the browser. That is for a person; --service-account is for an agent and creates the identity as it goes. Roles belong to a workspace, so member in one is not member in another, and the last account able to administer a workspace cannot be removed from it.

docs/hosting.md is the whole recipe: the service account, the systemd unit, nginx and Caddy, when to move off SQLite, giving an agent a token narrower than your own, backups on a separate volume, and what upgrading actually involves. Every command on that page has been run, including the refusals.

source_url in GET /v1/meta says where the source of this instance can be had. Nothing in the licence requires that of you — it is a promise the product makes to whoever is using it, and it is a setting so that somebody running a fork can point at theirs rather than at this repository.

Documentation

  • docs/connecting.md — the five ways to reach an instance, organised by which one you are. Start here if somebody has handed you an address and a token.
  • docs/hosting.md — running it as a service, end to end.
  • CHANGELOG.md — what changed, and which releases need a database migration. That last part is checked rather than remembered: CI refuses a release that moves the schema without saying so, so you can plan the upgrade instead of discovering it.
  • docs/errors.md — every error code the API can return. Generated from the registry, so it cannot drift from the code.
  • GET /v1/docs/agent — the guide an agent should read first, written for that reader.
  • SECURITY.md — how to report a vulnerability privately, and what is in scope. Not through an issue: an issue is public from the moment it is filed.

The full specification — data model, API, permissions and agent design — is written but not yet published. It lands here once the API has settled enough to be worth reading.

Contributing

Not code, for now — the core is still moving and there is no stable surface to review outside work against fairly. CONTRIBUTING.md says so at more length, and says what is welcome: bug reports, and being told why you stopped using it.

Licence

FSL-1.1-ALv2 — the Functional Source License.

Run it, modify it, fork it, for any purpose including making money. A person, a team, a five-hundred-person company self-hosting it for its own work, a consultancy charging to set it up for a client: all free, for ever, with nothing to buy and nobody to ask.

The one thing you may not do is sell other people access to it as a service. That is the whole restriction, and it is why this is source-available rather than an OSI open-source licence — the Open Source Definition does not allow a licence to rule out a field of use, even one.

Every release becomes Apache-2.0 two years after it ships, automatically, with no decision by anybody. That is the promise underneath the restriction: if this project goes somewhere you do not want to follow, you can take it and go.

Versions up to and including 0.5.0 were published under AGPL-3.0-or-later and remain so.

A commercial licence is available by agreement. If you want to offer Subroutine as a service, write to simon.holliday@protonmail.com and say what you have in mind.

Release files for subroutine 0.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for subroutine 0.6.0
File Size Uploaded
subroutine-0.6.0.tar.gz 1.3 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for subroutine 0.6.0
File Interpreter ABI Platform
subroutine-0.6.0-py3-none-any.whl Python 3 none any Details

Total release size: 2.1 MB

Release files / subroutine-0.6.0.tar.gz

Download URL subroutine-0.6.0.tar.gz
Size 1.3 MB
Tags Source
SHA-256 checksum
How to use checksums
d5b0479a7608758c370992dc999e5f1bf4b96ae8eeed72b5025eba511bc6a5c7
BLAKE2b-256 checksum
How to use checksums
25e7221ec48e6a274d68739472ee0355e53a51ff2e464d8d27558ed581543299
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 9, 2026.

Transparency log

Release files / subroutine-0.6.0-py3-none-any.whl

Download URL subroutine-0.6.0-py3-none-any.whl
Size 751.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
49bf056a5ad3c0ec5435922bcd4f0278823ff64ef18ad95f82732858a7fe0867
BLAKE2b-256 checksum
How to use checksums
3fe5a5a0e334efcf456a041b7b0d46000c410452e68fa4b017e6e6760ffc9112
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 9, 2026.

Transparency log

Release history Release notifications | RSS feed

0.9.0

2 release files

0.8.29

2 release files

0.8.27

2 release files

0.8.18

2 release files

0.8.7

2 release files

0.8.2

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.5

2 release files

0.7.1

2 release files

0.6.4

2 release files

0.6.3

2 release files

This release

0.6.0 This release

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page