subroutine commands
The 35 commands subroutine runs directly, each with its arguments and options.
subroutine add
subroutine add [OPTIONS] [words]...
Add something to your list.
Examples:
subroutine add "Call the dentist before Sunday"
subroutine add "Write the report by friday !3 ~2h #work"
subroutine add "Dates render as if this year" --type bug
subroutine add "Cache the roster" --description "Measured at 400ms a call."
subroutine add "Pay the rent" --repeat "every month on the 30th"
subroutine add "Water the plants tomorrow" --repeat "every 3 days" --repeat-from completion
Write the title as an instruction — 'Cache the roster', not 'The roster is cached'. A bug says what is wrong now instead, as the third example does, and a question ends in a question mark: three moods, so a list reads at a glance. '--description' is where the reasoning goes, which is what lets a title stay that short.
'--repeat-from schedule' keeps the rhythm whatever you do — rent is due on the 30th whether or not last month's was paid late. '--repeat-from completion' measures from when you finished, which is what "every three days" means about watering.
Arguments
words...(str): What you need to do.
Options
--type(str): task, bug, feature, chore, question, event. Defaults to task.--description(str): What it is about, in full. The title stays one line.--under(int): File it underneath this item, by number.--repeat(str): How often it comes round, like 'every other tuesday'.--repeat-from(str): Measure the next one from 'schedule' or from 'completion'. Defaults to schedule.--json(flag): Print the result as JSON.
subroutine agenda
subroutine agenda [OPTIONS] [when]
Show what you are doing today, or what another day looks like.
Examples:
subroutine agenda
subroutine agenda tomorrow
subroutine agenda saturday --days 2
subroutine -w work agenda
subroutine -w work agenda --project acme
A named day is shown as it stands now, so anything already late appears under Overdue whether or not it was late on that day.
Arguments
when(str): A day — 'tomorrow', 'friday', '+2w', '2026-08-01'. Default today.
Options
--days(int range, default7): How far ahead the look-ahead section reaches.--project(str): One project and everything under it. Needs -w.--json(flag): Print the agenda as JSON.--strict(flag): Stop if any connection cannot be reached.
subroutine today
subroutine today [OPTIONS]
Not listed by subroutine --help.
Say where this went. It is not an alias and does not print an agenda.
'subroutine agenda' is the command now.
subroutine list
subroutine list [OPTIONS]
List everything still open — tasks and documents — newest first.
Examples:
subroutine list
subroutine list --limit 10
subroutine list --order -priority_score
subroutine list --project SR --order due_at
subroutine list --assignee si --status blocked
subroutine list --claimed-by claude --order claimed_at
subroutine list --filter created_at.gte=yesterday
subroutine list --filter completed_at.gte=2026-08-02 --filter completed_at.lt=today
Options
--limit(int, default50): How many to show.--json(flag): Print the list as JSON.--merged(flag): One list rather than a group per connection.--strict(flag): Stop if any connection cannot be reached.--order(str): Sort by, e.g. '-priority_score' or 'due_at,-importance'.--project(str, repeatable): Only this project, by key.--connection(str): Only this connection, by name.--deferred(flag): Include things you have put off until a later date.--ready(flag): Only what you could start now — nothing unfinished blocks it.--to-act-on(flag): Only what is yours to act on — yours, nobody's, or held by you.--trash(flag): Show what you have deleted, instead of the list.--assignee(str, repeatable): Only what is assigned to somebody. A username, or 'me'.--claimed-by(str, repeatable): Only what somebody is holding now. A username, or 'me'.--status(str, repeatable): Only this status, e.g. 'blocked'.--type(str, repeatable): Only this type, e.g. 'bug'.--tag(str, repeatable): Only what carries this tag, without the '#'.--filter(str, repeatable): Narrow by a field — 'importance.gte=4', 'tag.in=ops,web', 'assignee.is=unset', 'created_at.gte=yesterday'. Repeat to narrow further: a range, or one field twice.
subroutine search
subroutine search [OPTIONS] [terms]
Find things by any of their words, wherever they were written.
Searches tasks and documents together, like 'subroutine list', because one number names either and a search that found only half of them would be lying about the rest.
A tag is found by writing it as you wrote it. Quote it, or the shell reads the '#' as the start of a comment. That finds what carries the tag and anything that mentions it in writing; '--tag' is the narrower question and answers with everything carrying it and nothing else.
Examples:
subroutine search "dentist"
subroutine search "#errand"
subroutine search "pagination" --project SR
subroutine search "boiler" --filter created_at.gte=yesterday
Arguments
terms(str): What to look for. See 'explain searching'.
Options
--limit(int, default50): How many to show.--json(flag): Print the results as JSON.--merged(flag): One list rather than a group per connection.--strict(flag): Stop if any connection cannot be reached.--order(str): Sort by, e.g. '-priority_score' or 'due_at,-importance'.--project(str, repeatable): Only this project, by key.--tag(str, repeatable): Only what carries this tag, without the '#'.--connection(str): Only this connection, by name.--deferred(flag): Include things you have put off until a later date.--filter(str, repeatable): Narrow by a field — 'importance.gte=4', 'tag.in=ops,web', 'assignee.is=unset', 'created_at.gte=yesterday'. Repeat to narrow further: a range, or one field twice.
subroutine journal
subroutine journal [OPTIONS]
What happened over a period, with who did it and what they said.
'subroutine changes' says what *moved* and is what you resume from a number. This says what *happened* — the same events, with the comments people wrote, the names of who did each thing, and what a change moved between rather than which rows it touched.
Ask it for a period. It is the question to ask when somebody wants writing up.
Examples:
subroutine journal --filter created_at.gte=yesterday
subroutine journal --filter created_at.gte=2026-08-28 --filter created_at.lt=2026-08-29
subroutine journal --by claude --filter created_at.gte=start_of_week
subroutine journal --oldest --filter created_at.gte=today
Options
--filter(str, repeatable): Which period, e.g. 'created_at.gte=yesterday'. Repeat for a range.--by(str): Only what one account did, by name.--mine(flag): Only what this machine's own credential did.--oldest(flag): Read the period forwards, in the order it happened.--limit(int, default50): How many to show.--json(flag): Print the entries as JSON.--strict(flag): Stop if any connection cannot be reached.
subroutine changes
subroutine changes [OPTIONS]
What has changed, oldest first — the question to ask after time away.
'subroutine list' says what is open now. This says what *moved*, which is the thing you cannot work out by looking at the current state.
'--by' is how you find out what somebody else has been doing, which is usually an agent you handed work to. '--mine' is the same question about this machine.
'--since' resumes where you left off; '--filter' asks about a period. They are different questions — you have no number to offer for 'what happened yesterday'.
Examples:
subroutine changes
subroutine changes --since 412
subroutine changes --filter created_at.gte=yesterday
subroutine changes --mine
subroutine changes --by claude
Options
--since(int): Carry on from this number, printed by the last run.--mine(flag): Only what this machine's own credential did.--by(str): Only what one account did, by name. Try it with an agent's name.--filter(str, repeatable): Narrow to a period, e.g. 'created_at.gte=yesterday'. Repeat for a range.--limit(int, default50): How many to show.--json(flag): Print the events as JSON.--strict(flag): Stop if any connection cannot be reached.
subroutine ls
subroutine ls [OPTIONS]
Not listed by subroutine --help.
The short name for 'subroutine list'. Both do the same thing.
Examples:
subroutine ls
Options
--limit(int, default50): How many to show.--json(flag): Print the list as JSON.--merged(flag): One list rather than a group per connection.--strict(flag): Stop if any connection cannot be reached.--order(str): Sort by, e.g. '-priority_score' or 'due_at,-importance'.--project(str, repeatable): Only this project, by key.--connection(str): Only this connection, by name.--deferred(flag): Include things you have put off until a later date.--ready(flag): Only what you could start now — nothing unfinished blocks it.--to-act-on(flag): Only what is yours to act on — yours, nobody's, or held by you.--trash(flag): Show what you have deleted, instead of the list.--assignee(str, repeatable): Only what is assigned to somebody. A username, or 'me'.--claimed-by(str, repeatable): Only what somebody is holding now. A username, or 'me'.--status(str, repeatable): Only this status, e.g. 'blocked'.--type(str, repeatable): Only this type, e.g. 'bug'.--tag(str, repeatable): Only what carries this tag, without the '#'.--filter(str, repeatable): Narrow by a field — 'importance.gte=4', 'tag.in=ops,web', 'assignee.is=unset', 'created_at.gte=yesterday'. Repeat to narrow further: a range, or one field twice.
subroutine show
subroutine show [OPTIONS] [which]
Read one item — what it is, what it is joined to, and what happened to it.
Works on a task or on a document, because one counter per workspace serves both and a number on a command line does not say which it is.
Examples:
subroutine show 42
subroutine show 42 --json
subroutine show 42 --history
subroutine show 42 --tree
'--tree' walks what has to happen before this can, indented by how deep it sits. On a milestone that is its contents, since a milestone is an item whose blockers are its parts — so it is how you read a plan without opening every item in it.
Arguments
which(str): An item number, as shown by 'subroutine list'.
Options
--history(flag): Every change, newest first.--tree(flag): What has to happen first, all the way down.--json(flag): Print as JSON.
subroutine claim
subroutine claim [OPTIONS] [which]
Not listed by subroutine --help.
Take something, so nobody else starts it too.
Examples:
subroutine claim 42
subroutine release 42
For when more than one person or agent works from the same list. A claim expires on its own, so nothing is stranded if whoever took it never comes back — say it again to hold it for longer.
Work somebody else has claimed disappears from 'subroutine list --ready' until their claim runs out. Your own never disappears from your own.
Arguments
which(str): A task number, as shown by 'subroutine list'.
Options
--minutes(int, default0): How long to hold it. Defaults to this instance's setting.
subroutine release
subroutine release [OPTIONS] [which]
Not listed by subroutine --help.
Put something back, so somebody else can pick it up.
Examples:
subroutine release 42
subroutine release --all
Releasing something nobody had claimed is not an error, so this is safe to run when you are not sure. Anybody who can change the task can release it — which is what makes an agent that died mid-task somebody else's problem to solve rather than nobody's.
'--all' is what a session-end hook runs. It says nothing when there is nothing to give back, which is the ordinary case once work has been finished.
Arguments
which(str): A task number, as shown by 'subroutine list'.
Options
--all(flag): Give back everything you are holding, wherever it is.
subroutine verify
subroutine verify [OPTIONS] [which]
Not listed by subroutine --help.
Record what you checked against something, so the next person can see it.
Examples:
subroutine verify 42 --summary "5,610 passed, 41 skipped"
subroutine verify 42 --failed --summary "3 failed in test_agenda"
This is a record, not a proof — anybody can say a check passed without running one. What it is worth is being kept, attributed, and able to go out of date: it carries the state of the code it ran against, so somebody reading it later can tell whether the code has moved since.
In a git checkout the tree is read from git unless you name one. Outside one there is nothing to read, and the record is kept without it — it simply cannot go out of date.
Arguments
which(str): A task number, as shown by 'subroutine list'.
Options
--summary(str): What was run, in one line.--failed(flag): Record a check that did not pass.--tree(str): The tree it ran against. Read from git here when omitted.--commit(str): The commit it ran against.
subroutine start
subroutine start [OPTIONS] [which]
Say you have started something.
Examples:
subroutine start 42
subroutine stop 42
A person could finish work and put work off and never say they were doing it. The one state that answers "what am I in the middle of" was reachable only over the API.
Arguments
which(str): A task number, as shown by 'subroutine list'.
subroutine stop
subroutine stop [OPTIONS] [which]
Say you have put something down again, without finishing it.
Examples:
subroutine stop 42
A state you can enter and not leave is worse than no state, which is why this exists beside 'start' rather than after somebody has asked for it. Picking something up and putting it down is ordinary; having to finish it to stop showing as busy is not.
Arguments
which(str): A task number, as shown by 'subroutine list'.
subroutine done
subroutine done [OPTIONS] [which]
Tick something off.
Examples:
subroutine done 42
subroutine done 42 --because "superseded by #99"
Arguments
which(str): A task number, as shown by 'subroutine list'.
Options
--because(str): Why, recorded against it.
subroutine skip
subroutine skip [OPTIONS] [which]
Let one of a repeating task go by, and bring the next.
Examples:
subroutine skip 42
subroutine skip 42 --because "away that week"
Arguments
which(str): A task number, as shown by 'subroutine list'.
Options
--because(str): Why, recorded against it.
subroutine plan
subroutine plan [OPTIONS] [which] [when]
Say which day you will do something.
'--until' is for something that lasts — a holiday, a conference, a code freeze.
Examples:
subroutine plan 1 tomorrow
subroutine plan 7 "14 august" --until "28 august"
subroutine plan 42 friday --because "the review is on monday"
Arguments
which(str): A task number, as shown by 'subroutine list'.when(str, default'\x00not given'): A day — 'today', 'tomorrow', 'friday', '2026-08-01'. Pass '' to clear it.
Options
--until(str, default'\x00not given'): The last day of it, if it lasts more than one. Pass '' to clear it.--just-this-one(flag): If it repeats: change this one only.--from-now-on(flag): If it repeats: change this one and every one after it.--because(str): Why, recorded against it.
subroutine defer
subroutine defer [OPTIONS] [which] [when]
Hide something until later.
A day on its own hides it until that morning. Write a time as well and it comes back at that time — your agenda still waits for the day to turn.
Examples:
subroutine defer 1 monday
subroutine defer 7 "2026-08-18 06:00"
subroutine defer 42 2026-09-01 --because "waiting on the provider's reply"
Arguments
which(str): A task number, as shown by 'subroutine list'.when(str): A day to hide it until, or a day and a time.
Options
--just-this-one(flag): If it repeats: change this one only.--from-now-on(flag): If it repeats: change this one and every one after it.--because(str): What you are waiting for, recorded against it.
subroutine move
subroutine move [OPTIONS] [which]
Make something part of another item, or a top-level item again.
Examples:
subroutine move 42 --under 7
subroutine move 42 --top
Arguments
which(str): A number, as shown by 'subroutine list'.
Options
--under(str): The number of the item this becomes a part of.--top(flag): Make it a top-level item instead.
subroutine update
subroutine update [OPTIONS] [which]
Change what a task says about itself.
Everything you do not name is left alone.
Examples:
subroutine update 42 --importance 4 --urgency 3
subroutine update 42 --estimate 2h --type bug
subroutine update 42 --assignee jo --due friday
subroutine update 42 --title "Fix the parser, not the tokeniser"
subroutine update 42 --repeat "every other tuesday"
subroutine update 42 --repeat ""
A repeat belongs to the series rather than to the one in front of you, so changing it changes every occurrence after this one. '--repeat ""' stops it: the work in hand keeps its number and its history, and nothing follows it.
'--expected-version' turns the change down if somebody has saved since. The number comes from 'subroutine show 42 --json', which is where a script reads it; the plain command does not print it, because a version is machinery rather than something you set. Without it the last save wins and the other person's edit goes with no record that it happened.
Arguments
which(str): A task number, as shown by 'subroutine list'.
Options
--title(str): What it is called.--description(str, default'\x00not given'): What it is about. Pass '' to clear it.--importance(int, default-1): How much it matters, 1-5.--urgency(int, default-1): How soon, 1-5.--estimate(str, default'\x00not given'): How long, like '2h' or '90m'. Pass '' to clear it.--remind(str, default'\x00not given'): How long before, like '2w' or '1h'. Pass '' to clear it.--type(str): task, bug, feature, chore, question, event.--status(str): A status, like 'blocked'.--project(str): File it under this project, by key.--assignee(str, default'\x00not given'): Who is to do it, by username. Pass '' to leave it with nobody.--tags(str, default'\x00not given'): Replace its tags, comma-separated. Pass '' to remove them all.--due(str, default'\x00not given'): When it is due, like 'friday' or '2026-08-20'. Pass '' to clear it.--timezone(str, default'\x00not given'): The zone the deadline is read in.--repeat(str, default'\x00not given'): How often it comes round. Pass '' to stop it repeating.--repeat-from(str, default'\x00not given'): Measure the next one from 'schedule' or from 'completion'.--just-this-one(flag): If it repeats: change this one only.--from-now-on(flag): If it repeats: change this one and every one after it.--expected-version(int, default-1): Refuse the change if the task has moved on. See 'show --json'.--because(str): Why, recorded against it.--json(flag): Print the result as JSON.
subroutine comment
subroutine comment [OPTIONS] [which] [body]
Record what happened against an item.
A comment is what you *did*; a document is what you concluded. If the next session would need to read it, write it down properly instead.
Examples:
subroutine comment 42 "ran the suite, two failures in the date parser"
Arguments
which(str): An item number, as shown by 'subroutine list'.body(str): What happened.
subroutine uncomment
subroutine uncomment [OPTIONS] [which] [words]
Take a comment back out of an item's record.
Examples:
subroutine uncomment 42 "two failures in the date parser"
Named by what it says, because that is what you are looking at. A comment has no number of its own and its id is a UUID that appears in nothing a person reads — so asking for one would make this a command only a script could run.
Matching more than one is refused rather than guessed at: say more of the sentence. Deleting rather than editing is deliberate. A comment is attributed prose, and rewriting somebody's words under their name is not a thing to be able to do.
Arguments
which(str): An item number, as shown by 'subroutine list'.words(str): Words from the comment you want taken out.
subroutine link
subroutine link [OPTIONS] [which] [relation] [other]
Say how two items are related.
Examples:
subroutine link 42 blocks 43
subroutine link 42 blocks 43,44,45
subroutine link 43,44,45 blocks 42
subroutine link 42 relates-to 12
subroutine link 7 documents 42
'blocks' is the one that changes what you see: 'subroutine list --ready' leaves out anything blocked by unfinished work, so this is how that filter learns anything.
'documents' is the one that says a decision governs a piece of work, so that whoever picks #42 up can be shown what they have to read before starting it.
Those are the five a new workspace is given. A workspace can rename them or add its own, and naming one this workspace does not have lists the ones it does.
Several numbers separated by commas make one link each, all of the same kind. Either side takes them, and both sides at once means every one of the first joined to every one of the second — which is what 'each of these blocks each of those' says and is the only thing it could say.
Both sides matter because a plan is written from both ends: 'these six make up the roadmap' is six things blocking one, and 'this has to happen before those three' is one blocking three. Laying out a plan is the moment this is most heavily used, and it is the moment one link per command costs most.
Arguments
which(str): Which item, by its number. Or several: 9,11,12.relation(str): blocks, relates-to, duplicates, derives-from, documents, supersedes.other(str): The other item, by its number. Or several: 9,11,12.
subroutine unlink
subroutine unlink [OPTIONS] [which] [other]
Undo a link between two items.
Examples:
subroutine unlink 42 43
subroutine unlink 42 43,44,45
subroutine unlink 42 43 --type blocks
Worth having beside 'link' rather than later. A link added by mistake blocks work that is not blocked, and --ready then hides it — so an unwanted link is worse than a missing one, because it narrows what looks startable and says nothing about doing so.
You do not have to say which kind, because usually there is only one and having to remember the relation is what leaves a wrong link in place. Where two items are joined more than one way, this says so and lists them rather than removing both.
Several numbers separated by commas undo one link each, which is what a plan laid out the wrong way round needs.
Arguments
which(str): Which item, by its number.other(str): The item it is joined to. Or several: 9,11,12.
Options
--type(str): Which kind to undo, when there is more than one: blocks, relates-to, duplicates, derives-from, documents, supersedes.
subroutine delete
subroutine delete [OPTIONS] [which]
Take something off the list that should not have been on it.
Examples:
subroutine delete 42
It goes to the trash rather than vanishing, so it can be put back — the wrong number is the commonest mistake anybody makes here, and the second commonest is making it twice.
Arguments
which(str): Which one, by its number.
subroutine restore
subroutine restore [OPTIONS] [which]
Put something back that was deleted.
Examples:
subroutine restore 42
subroutine list --trash
Arguments
which(str): Which one, by its number.
subroutine use
subroutine use [OPTIONS] [where]
Not listed by subroutine --help.
Choose what a bare task number means.
It changes what a number means. It never changes what you can see — every listing still spans everything you can reach.
Examples:
subroutine use
subroutine use acme
subroutine use work/acme
subroutine use --here --project SR
subroutine use --reset
'--here' writes a .subroutine file in the current directory, and is what a checkout of a repository wants: it answers "which project is this work" for everything started from here, including an agent, which cannot be asked. Without it the choice is machine-wide, which cannot be right for two repositories at once.
Arguments
where(str): A workspace, or 'connection/workspace'.
Options
--here(flag): Write it into this directory instead, for this checkout.--project(str): With --here: file new work under this project, by key.--reset(flag): Go back to the configured default.
subroutine whoami
subroutine whoami [OPTIONS]
Which account this machine is acting as, and what it is allowed to do.
Examples:
subroutine whoami
subroutine whoami --json
Worth asking before the first change of a session. One machine can hold more than one credential — yours in the credentials file, an agent's in the environment — and the one that answers here is the one your next command will act under.
Options
--json(flag): Print the answer as JSON.--strict(flag): Stop if any connection cannot be reached.
subroutine init
subroutine init [OPTIONS]
Set up Subroutine: create the database and everything a first task needs.
Options
--username(str): Who you are. Defaults to your system username.--workspace(str, default'Projects'): What to call your first workspace.--instance-name(str): What to call this installation. Defaults to the hostname.--password-stdin(flag): Read a password from standard input, for scripts.--non-interactive(flag): Never prompt; fail instead.--verbose(flag): Print what was created.
subroutine mcp
subroutine mcp [OPTIONS]
Serve this instance to an AI agent over MCP, on stdin and stdout.
Examples:
subroutine mcp
subroutine mcp --connection work
subroutine mcp --connection work --workspace acme
Speaks the Model Context Protocol over stdio, so a client starts this as a child process rather than connecting to a port. There is nothing to expose and no listener: if the client is not running it, nothing is serving.
One connection, chosen here. Unlike 'agenda', which merges every configured instance because a person has one day, a tool call writes somewhere — and where it writes has to be a decision you can see rather than one this process takes for you.
'--workspace' is the same argument one level down, and it matters as soon as an instance holds two: without it every read is refused as ambiguous, and the agent has no way to learn a name it was never told. It is a default rather than a limit — a call naming a workspace still goes there, and a token pinned to one narrows it for real.
Options
--connection(str): Which instance to work in. Defaults to the current one.--workspace(str): Which workspace its calls land in. Unset means say each time.
subroutine serve
subroutine serve [OPTIONS]
Serve the HTTP API.
Examples:
subroutine serve
subroutine serve --host 0.0.0.0 --insecure
There is no setting that turns the API on or off, deliberately: if this process is not running there is no socket, and a configuration key that made 'serve' refuse to start would be a confusing way of saying "do not run it". The control that actually controls anything is the bind address, and its default is loopback.
Options
--host(str): What to listen on. Defaults to 127.0.0.1.--port(int, default0): Which port to listen on. Defaults to the one in 'subroutine config show'.--insecure(flag): Listen beyond this machine without TLS. Say this out loud, or set public_url.--log-level(str): How much to log.
subroutine upgrade
subroutine upgrade [OPTIONS]
Not listed by subroutine --help.
Say where this went. It is not an alias and does not upgrade anything.
'subroutine db upgrade' is the command now.
subroutine help
subroutine help [OPTIONS]
Show what this can do — the same as 'subroutine --help'.
The same answer, deliberately. This used to explain concepts while '--help' listed commands, so one question had two answers and the reader had to learn which was which before learning either. 'help' is what everybody types first, so it answers the commonest question; the concepts moved to 'explain', whose name says what it is for in a way 'help <topic>' never did.
subroutine explain
subroutine explain [OPTIONS] [topic]
Explain a concept — refs, dates, the capture grammar, scripting.
Examples:
subroutine explain
subroutine explain dates
Arguments
topic(str): A concept to explain. Omit to list them.
subroutine doctor
subroutine doctor [OPTIONS]
Say whether this machine's installation is coherent, and change nothing.
Examples:
subroutine doctor
What is running and where it came from, which configuration it is reading, what each connection answers, and when a backup was last taken. One command because getting it wrong is nearly always the same mistake: running something without the environment the service uses, which acts on a different database and looks exactly like success.
It exits non-zero if anything needs attention, so it can be the last line of an update script rather than something a person reads and forgets.
It talks to the instances you have configured and to nothing else. Whether a newer release exists is a different question, asked with 'subroutine db upgrade --check'.