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 PROFILENOTFOUND 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 |
| PROFILENOTFOUND |
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 |