op-cli
A resolving CLI for OpenProject: every flag accepts a human name or an id,
and the tool turns names into ids itself. Run any command below with
--help for its full reference; this file teaches only what it cannot.
Boundary (read first)
- If the CLI lacks a command for what you need, stop and report that gap to
the user. Do not fall back to
curl, hand-built HTTP calls, or reads of
credentials.json; there is no passthrough by design.
- Deletions need an explicit
--yes: wp delete <id> --yes,
time delete <id> --yes, project delete <reference> --yes. They are
irreversible; never work around a refused deletion through another
route. Deleting a work package cascades on the server: its descendants
are deleted with it, and wp delete names the descendant count in both
its refusal and its confirmation. There is no user delete; report
that gap instead of improvising.
- Five conventions are easy to guess wrong:
- A project in context narrows what you read:
--project, or the
profile default, scopes wp list, wp count, time list, and
time report to that project and its subprojects. Without one, those
four report instance-wide.
--field "Estimate=5" sets a custom field by human name;
--field "Estimate=" clears it.
--all streams NDJSON: one JSON record per line, not an array.
--fields id,subject narrows single-record and collection-row
commands: wp get/create/update, wp history/comments/
relations, time log/get/update, project get/create/
update/copy/member. List and count commands print fixed
columns instead, and wp create --stdin refuses the flag; without
--fields the JSON output carries the whole record.
- A truncated list warns on stderr ("Showing X of Y records. Pass --all")
while data stays on stdout and the exit code stays 0.
- Terminal tables are bounded to the terminal width, long cells cut
with an ellipsis; JSON output is never truncated.
Session start
export OP_CLI_OUTPUT=json
export OP_CLI_NO_UPDATE_CHECK=1
Set JSON output once for the whole session; suppress the once-a-day
update notice that would otherwise pollute stderr. Errors render as
JSON objects carrying a stable code. Do not check auth upfront: run
the command you need directly. If it fails with PROFILE_NOT_FOUND or
AUTH_FAILED, run op-cli auth login (interactive prompts) and retry.
Intent to command
# find work packages; repeat a filter flag to OR values
# --created-after and --updated-after take the same date forms
op-cli wp list --open --type Task --assignee me --updated-after 7d
# find by words in the subject, server-side
op-cli wp list --search login
# how many match, without paginating; fetch every page with --all
op-cli wp count --priority High
# inspect one work package, optionally narrowing columns
op-cli wp get <id> --fields id,subject,status
# create one work package with values given by name
op-cli wp create Subject --type Bug --priority High
# set a custom field by name; an empty value after = clears it
op-cli wp update <id> --field Estimate=5
# change status, assignee, version, category, priority
op-cli wp update <id> --status Closed --assignee me
# build hierarchies: nest under a parent work package
op-cli wp create Sub-task --parent <id>
op-cli wp update <id> --parent <parent-id>
# give a work package a markdown body
op-cli wp create <subject> --description <markdown>
# delete needs explicit confirmation; bulk-create from a JSON array
op-cli wp delete <id> --yes
echo '[{"subject":"First"},{"subject":"Second"}]' | op-cli wp create --stdin
# discuss and audit a work package
op-cli wp comment <id> <text>
op-cli wp history <id>
# relations between work packages
op-cli wp relations <id>
op-cli wp relate <id> <to>
# which fields exist for a work package's project and type
op-cli wp schema <id>
# log and manage time; hours accept 1.5, 1h30m, PT1H30M
op-cli time log <id> --hours 1h30m --activity Development
op-cli time list --wp <id> --from today
# report a closed period; --to is inclusive of its day, alone or with --from
op-cli time report --from 7d --to yesterday --user me
# projects: search, inspect, create with explicit identifier, copy
op-cli project list --search web
op-cli project create Web --identifier web
op-cli project copy <reference> Copy --identifier web-copy
# add or remove project members; without membership, assignment is refused
op-cli project member add <project> <user> <role>
op-cli project member remove <project> <user>
# move a work package to another project, by id, identifier, or name
op-cli wp move <id> <project>
# what a work package may use in a project
op-cli project types <reference>
# project vocabulary: members, versions, categories, activities, fields;
# a project context is required: pass --project <id> or set a default
# project on the profile (meta types, statuses, priorities are instance-wide)
op-cli meta members --project <id>
op-cli meta fields
# something does not work: diagnose connectivity, credentials, versions
op-cli doctor
# self-update (prints the right command for Volta and Homebrew installs)
op-cli update
Error contract
Read [CODE] on stderr (or code in JSON); do not match English prose.
Codes are a closed set with stable exit codes, 0 through 7.
| Code |
Exit |
Recovery action |
| USAGE_ERROR |
1 |
fix flags or arguments; run the command again with --help |
| PROFILE_NOT_FOUND |
1 |
run op-cli auth login; env vars alone also work |
| API_ERROR |
2 |
read the message: a rejected write needs different values, a failed request may be worth retrying |
| INTERNAL_ERROR |
2 |
retry once; if it persists, report with op-cli doctor output |
| AUTH_FAILED |
3 |
credentials or permissions changed; run op-cli auth login |
| NOT_FOUND |
4 |
check the id; run op-cli meta refresh if names changed recently |
| CONFLICT |
5 |
someone edited the same work package; re-read, merge, retry |
| NETWORK_ERROR |
6 |
check URL and network; after a write, state is unknown, verify first |
| UNSUPPORTED_VERSION |
7 |
instance needs OpenProject v13+ (API v3); upgrade or report |
1---2name: op-cli3description: Drive OpenProject from an agent through the op-cli command-line client. Use when a task involves OpenProject, projects, work packages, time entries or time logging, instance metadata such as types and statuses, or any op-cli subcommand; resolves human names to ids and reports machine-readable error codes.4---56# op-cli78A resolving CLI for OpenProject: every flag accepts a human name or an id,9and the tool turns names into ids itself. Run any command below with10`--help` for its full reference; this file teaches only what it cannot.1112## Boundary (read first)1314- If the CLI lacks a command for what you need, stop and report that gap to15 the user. Do not fall back to `curl`, hand-built HTTP calls, or reads of16 `credentials.json`; there is no passthrough by design.17- Deletions need an explicit `--yes`: `wp delete <id> --yes`,18 `time delete <id> --yes`, `project delete <reference> --yes`. They are19 irreversible; never work around a refused deletion through another20 route. Deleting a work package cascades on the server: its descendants21 are deleted with it, and `wp delete` names the descendant count in both22 its refusal and its confirmation. There is no `user delete`; report23 that gap instead of improvising.24- Five conventions are easy to guess wrong:25 - A project in context narrows what you read: `--project`, or the26 profile default, scopes `wp list`, `wp count`, `time list`, and27 `time report` to that project and its subprojects. Without one, those28 four report instance-wide.29 - `--field "Estimate=5"` sets a custom field by human name;30 `--field "Estimate="` clears it.31 - `--all` streams NDJSON: one JSON record per line, not an array.32 - `--fields id,subject` narrows single-record and collection-row33 commands: `wp get`/`create`/`update`, `wp history`/`comments`/34 `relations`, `time log`/`get`/`update`, `project get`/`create`/35 `update`/`copy`/`member`. List and count commands print fixed36 columns instead, and `wp create --stdin` refuses the flag; without37 `--fields` the JSON output carries the whole record.38 - A truncated list warns on stderr ("Showing X of Y records. Pass --all")39 while data stays on stdout and the exit code stays 0.40 - Terminal tables are bounded to the terminal width, long cells cut41 with an ellipsis; JSON output is never truncated.4243## Session start4445```sh46export OP_CLI_OUTPUT=json47export OP_CLI_NO_UPDATE_CHECK=148```4950Set JSON output once for the whole session; suppress the once-a-day51update notice that would otherwise pollute stderr. Errors render as52JSON objects carrying a stable `code`. Do not check auth upfront: run53the command you need directly. If it fails with `PROFILE_NOT_FOUND` or54`AUTH_FAILED`, run `op-cli auth login` (interactive prompts) and retry.5556## Intent to command5758```sh59# find work packages; repeat a filter flag to OR values60# --created-after and --updated-after take the same date forms61op-cli wp list --open --type Task --assignee me --updated-after 7d62# find by words in the subject, server-side63op-cli wp list --search login64# how many match, without paginating; fetch every page with --all65op-cli wp count --priority High66# inspect one work package, optionally narrowing columns67op-cli wp get <id> --fields id,subject,status68# create one work package with values given by name69op-cli wp create Subject --type Bug --priority High70# set a custom field by name; an empty value after = clears it71op-cli wp update <id> --field Estimate=572# change status, assignee, version, category, priority73op-cli wp update <id> --status Closed --assignee me74# build hierarchies: nest under a parent work package75op-cli wp create Sub-task --parent <id>76op-cli wp update <id> --parent <parent-id>77# give a work package a markdown body78op-cli wp create <subject> --description <markdown>79# delete needs explicit confirmation; bulk-create from a JSON array80op-cli wp delete <id> --yes81echo '[{"subject":"First"},{"subject":"Second"}]' | op-cli wp create --stdin82# discuss and audit a work package83op-cli wp comment <id> <text>84op-cli wp history <id>85# relations between work packages86op-cli wp relations <id>87op-cli wp relate <id> <to>88# which fields exist for a work package's project and type89op-cli wp schema <id>90# log and manage time; hours accept 1.5, 1h30m, PT1H30M91op-cli time log <id> --hours 1h30m --activity Development92op-cli time list --wp <id> --from today93# report a closed period; --to is inclusive of its day, alone or with --from94op-cli time report --from 7d --to yesterday --user me95# projects: search, inspect, create with explicit identifier, copy96op-cli project list --search web97op-cli project create Web --identifier web98op-cli project copy <reference> Copy --identifier web-copy99# add or remove project members; without membership, assignment is refused100op-cli project member add <project> <user> <role>101op-cli project member remove <project> <user>102# move a work package to another project, by id, identifier, or name103op-cli wp move <id> <project>104# what a work package may use in a project105op-cli project types <reference>106# project vocabulary: members, versions, categories, activities, fields;107# a project context is required: pass --project <id> or set a default108# project on the profile (meta types, statuses, priorities are instance-wide)109op-cli meta members --project <id>110op-cli meta fields111# something does not work: diagnose connectivity, credentials, versions112op-cli doctor113# self-update (prints the right command for Volta and Homebrew installs)114op-cli update115```116117## Error contract118119Read `[CODE]` on stderr (or `code` in JSON); do not match English prose.120Codes are a closed set with stable exit codes, 0 through 7.121122| Code | Exit | Recovery action |123|---|---|---|124| USAGE_ERROR | 1 | fix flags or arguments; run the command again with `--help` |125| PROFILE_NOT_FOUND | 1 | run `op-cli auth login`; env vars alone also work |126| API_ERROR | 2 | read the message: a rejected write needs different values, a failed request may be worth retrying |127| INTERNAL_ERROR | 2 | retry once; if it persists, report with `op-cli doctor` output |128| AUTH_FAILED | 3 | credentials or permissions changed; run `op-cli auth login` |129| NOT_FOUND | 4 | check the id; run `op-cli meta refresh` if names changed recently |130| CONFLICT | 5 | someone edited the same work package; re-read, merge, retry |131| NETWORK_ERROR | 6 | check URL and network; after a write, state is unknown, verify first |132| UNSUPPORTED_VERSION | 7 | instance needs OpenProject v13+ (API v3); upgrade or report |