A practical command-line toolkit for managing OpenProject work packages directly from your terminal. It helps with daily ticket handling, status updates, time tracking, project discovery, lightweight reporting, and team prioritization without leaving your shell.
- Link a local repository or folder to an OpenProject project with
op init - List your open work packages, grouped by project, as JSON or a readable table
- Inspect, update, close, and prioritize work packages from the command line
- Log time entries for today, yesterday, or any explicit date
- Generate daily JSON reports and monthly time-tracking calendars
- Create new work packages assigned to yourself
- Use Fish shell completion for faster command entry
- Bash
curl,jq,bc,base64, and standard Unix utilities- OpenProject API access token
- Optional:
tabulateforop list --table - Optional:
columnforop report --table - Optional: Fish shell for completion support
Set the following environment variables before using the CLI:
export OP_BASE_URL="https://openproject.example.com"
export OP_TOKEN="your-openproject-api-token"You can verify the connection with:
op healthFor project-aware commands, initialize the current directory once:
op init my-projectThis creates a local .op_info file containing the selected OpenProject project metadata.
| Command | Description | Useful Options |
|---|---|---|
op help |
List available operations discovered from lib/op_* scripts |
- |
op init <query> |
Link the current directory to an OpenProject project via .op_info |
- |
op project_list |
List all visible projects with id, identifier, name, active, and public |
PAGE_SIZE environment variable |
op list |
List open work packages assigned to you, grouped by project | --team, --table, `--version=<id |
op versions [query] |
List available versions, optionally filtered by ID or name fragment | Uses .op_info project context when present |
op pm <project> |
Project-management JSON view for a project | Accepts project ID, identifier, or name fragment |
op set_version <id> <version> |
Assign a work package to a version by version ID or name | Version can be a numeric ID or name |
op review |
Interactively review tickets and update status, priority, or completion | Same as op list, except --table |
op status [id] |
Show detailed work package metadata by explicit ID or current Git branch | Includes assigned version |
op wip [id] |
Set a work package status to in progress |
- |
op close [id] |
Set a work package status to closed |
- |
op log <id> <hours> ["comment"] |
Log time on a specific work package | --tegnap, --nap=YYYY-MM-DD |
op report [YYYY-MM-DD] |
Print your time entries for a day as grouped JSON | --table, optional date |
op calendar [--json] [month] |
Show a monthly worklog calendar as ASCII or JSON | --json, -1, 02, jan, 2024.11 |
op create "Title" ["Description"] |
Create a new work package assigned to you | --projectId=<id>, --parentId=<id> |
op parent <parent_id> |
Set the current branch ticket's parent | - |
op parent <id> <parent_id> |
Set a specific work package's parent | - |
op rename <id> "New title" |
Rename a work package by explicit ID | - |
op health |
Check OpenProject API connectivity | Exit code 0 on success, 1 on failure |
op version |
Print the CLI version | - |
op prio [options] <ids...> |
Raise selected tickets to high priority and put others on hold | --team, --dry-run |
op enum_status [name] |
Print status IDs as JSON, or only the ID for a given status name | - |
op queries |
Print the full OpenProject queries API response | - |
The Fish completion file is included at completions/op.fish.
Install it with:
mkdir -p ~/.config/fish/completions
cp completions/op.fish ~/.config/fish/completions/op.fishSee doc/fish-completion.md for details.
Lists every executable lib/op_* script as an available operation. Each command can also be inspected with op <command> help, which forwards to that command's built-in description.
Links the current directory to an OpenProject project. The command searches projects visible through op project_list by exact identifier match, case-insensitively, or by partial project name.
When exactly one project matches, it writes a .op_info file with:
project_idproject_identifierproject_name
If multiple projects match, the command lists the candidates and leaves the directory unchanged.
Fetches every project visible to the configured user, following OpenProject API pagination. The output is a JSON array containing id, identifier, name, active, and public fields.
Use the PAGE_SIZE environment variable to override the default page size of 100.
Lists open work packages using the OpenProject REST API filters parameter. By default, it returns open tickets assigned to the current user. If the current directory has a .op_info file, the command also filters by that project.
Each listed work package includes its assigned version name when one is set.
Use --team to remove the assignee filter and show all open team work packages. Use --version <id|name> to restrict the result set to a specific version. Use --table to render a readable ASCII table through tabulate; otherwise the output is grouped JSON by project.
Lists available versions through the OpenProject versions API. When .op_info exists, the command lists the versions available in that project. Without .op_info, it falls back to globally visible versions.
An optional query filters by exact ID, exact name, or partial name match.
The JSON output contains:
idnamestatusstartDateendDateproject
Builds a project-scoped JSON payload for management views without relying on assigned-to-me filtering.
The project argument is resolved by exact project ID, exact identifier, exact name, or partial match on identifier/name.
The JSON output contains:
project: resolved project metadataboard: open work packages grouped by version, with status, assignee, and spent hoursroadmap: project versions enriched with current open-ticket counts and spent hourstimeEntries: all project time entries, including abyPersonByDaybreakdown for later statistics or UI work
Assigns a work package to a version. The command resolves the version either by numeric ID or by name. For name-based matches, exact name is preferred; otherwise a unique partial match is accepted.
The command first fetches the current work package lockVersion, then updates the work package with PATCH and the _links.version.href field.
On success, it returns compact JSON with the work package ID, title, assigned version name, and version ID.
Runs an interactive review flow over the JSON output of op list. It requires an interactive TTY and lets you update each work package individually.
Editable fields:
- Status by name or ID;
?lists available statuses - Completion percentage via
percentageDone - Priority by name or ID
Approved changes are sent through the OpenProject API with PATCH requests.
Shows key metadata for an explicit work package ID, or for the first numeric ID found in the current Git branch name when no ID is provided.
The output now also includes the assigned version, plus the parent work package title and parent_id when the ticket is part of a hierarchy.
Displayed fields include project, subject, assigned version, optional parent, assignee, type, status, completion percentage, and spent time.
Sets the current or specified work package to in progress using status ID 6. The command first fetches the current lockVersion, then sends the update payload with a PATCH request.
Sets the current or specified work package to closed using status ID 10. On success, it prints #<id> closed..
Logs time on an explicit work package ID. Unlike op status, op wip, and op close, this command does not infer the work package ID from the current Git branch. The comment is optional.
Date options:
- No date option: log for today
--tegnap: log for yesterday--nap=YYYY-MM-DD: log for an explicit date
The hour value is converted to an ISO 8601 duration. For example, 3.5 becomes PT3H30M.
The payload includes links to the project, work package, and time entry activity /api/v3/time_entries/activities/9. A comment is included only when provided.
Prints a report of your own time entries for a given day. Without a date argument, it uses today. With a date argument, it validates and filters by the provided YYYY-MM-DD date. If .op_info exists, the report is also filtered to the linked project.
Default JSON output contains:
projects: an object keyed by project nameprojects.<name>.entries[]: entries withhours,workPackageId, andworkPackageTitleprojects.<name>.sumHours: total hours for that projectsumHours: total hours for the full report
Use --table to print a terminal table with project, work package ID, title, and hours, followed by the total hour count.
Shows a monthly worklog calendar. By default, it renders an ASCII calendar with Monday as the first day of the week. Each week is shown as a date range, and each workday cell contains the total logged hours for that day.
Color meaning in ASCII mode:
- Green:
>= 8h - Yellow:
0 < h < 8 - Red:
0h
Calendar behavior:
- Weekend cells are empty
- Future dates are empty
- Today is highlighted with stronger styling and a
>prefix - If
.op_infoexists, entries are filtered by the linked project; otherwise all personal time entries are included
Month selection:
- No argument: current month
MMor month name such asjan,feb: nearest past matching monthYYYY.MMorYYYY-MM: explicit month-N: month offset, for example-1or-4
Use --json for machine-readable output. The JSON response contains month metadata, daily sums in daySums, and weekly breakdowns in weeks[].days[] with date, weekday, and hours.
Creates a new work package and assigns it to the current user.
Project selection order:
- Explicit
--projectId=<id>or--projectId <id> - Project configured in
.op_info
Use --parentId=<id> or --parentId <id> to create the work package directly under an existing parent work package.
The command resolves the current user via /api/v3/users/me, sends the optional Markdown description and parent link when provided, and prints compact JSON with id, title, status, and parentId on success.
Sets the parent of the current branch work package. The command infers the child work package ID from the first numeric fragment in the current Git branch name.
Sets the parent of an explicit work package. The command first fetches the current lockVersion, then sends a PATCH request with the _links.parent update.
On success, it prints #<id> parent set to: #<parent_id> and includes the parent title when the API returns it.
Renames an explicit work package by updating its subject. The command first fetches the current lockVersion, then sends a PATCH request with the new title.
On success, it prints #<id> renamed to: <new title>.
Checks whether the OpenProject API is reachable with the configured OP_BASE_URL and OP_TOKEN by calling /api/v3/users/me.
Exit codes:
0: success1: invalid token, invalid URL, unavailable server, or another connection failure
Prints the current CLI version on a single line.
Prioritizes visible work packages from the op list result set. Arguments can be numeric work package IDs or case-insensitive subject fragments.
If a pattern matches multiple tickets, the command stops to prevent accidental updates. Selected tickets are raised to High priority, while every other visible ticket is moved to on hold.
Use --team to remove the assignee filter. Use --dry-run to show the planned changes without sending API updates.
Prints OpenProject status IDs. Without an argument, the command returns a JSON object in this form:
{
"status name": 1
}When a status name is provided, only the matching ID is printed. This is useful for shell scripts and automation around commands such as op prio.
Prints the full JSON response from the OpenProject queries endpoint. This is useful for auditing predefined views, discovering available queries, or building custom reports.
op health
op init my-project
op list --table
op versions
op list --version "Release 2.4" --table
op set_version 12345 "Release 2.4"
op status
op parent 12000
op wip
op rename 12345 "Updated title"
op log 12345 2.5 "Implemented API integration"
op report
op calendarop status,op wip, andop closeinfer the work package ID from the first number in the current Git branch name when no ID is provided.op parentcan either infer the child work package ID from the current Git branch or accept it explicitly.op renamealways requires an explicit work package ID.op logalways requires an explicit work package ID..op_infois local project metadata. Commit it only if that project binding is intentionally shared by the repository.- Some commands use fixed OpenProject status or activity IDs; adjust the scripts if your OpenProject instance uses different IDs.