OpenProject CLI
Single-command interface for OpenProject. All operations via op.py.
Quick Start
# From skill directory:
cd skills/openproject
# 1. First time: init config (only once per project)
uv run python op.py init 13
# 2. Check status
uv run python op.py status
# 3. Start working
uv run python op.py wp list --status openSetup
cd skills/openproject
uv syncConfigure .env:
OPENPROJECT_URL=https://your-instance.com
OPENPROJECT_API_KEY=your-api-keyAPI key from: OpenProject → My Account → Access Tokens
CLI Commands
All commands: cd skills/openproject && uv run python op.py <command>
System
| Command | Description |
|---|---|
status | Check connection + config status |
init <project_id> | Init config for project (once) |
config | Show full config (types, statuses, members, custom fields) |
Work Packages (wp)
| Command | Description |
|---|---|
wp list [options] | List work packages |
wp get <id> | Get single work package |
wp create --subject "..." [options] | Create work package |
wp update <id> [options] | Update work package |
wp delete <id> | Delete work package |
wp comment <id> --message "..." | Add comment |
wp activities <id> | List activities/comments |
wp relations <id> | List WP relations |
wp add-relation <from> <to> --type blocks | Create relation |
wp del-relation <relation_id> | Delete relation |
wp schema --type-id N | Get form schema |
wp list filters: --status open|closed|all|"name" --type "Task" --assignee "name" --parent N --project N --limit N
wp create/update: --subject --type --status --priority --assignee --responsible --description --parent --version --start-date --due-date --estimated-hours --done --cf "key=val"
relation types: relates|blocks|blocked|precedes|follows|duplicates|includes|partof|requires
Time Tracking (time)
| Command | Description |
|---|---|
time log <wp_id> --hours N | Log time entry |
time list [options] | List time entries |
time today | Today's entries for current user |
time get <id> | Get time entry |
time update <id> [options] | Update time entry |
time delete <id> | Delete time entry |
time activities | List activity types |
time log: --hours N (required) --comment "..." --date YYYY-MM-DD --activity N
time list: --wp N --user "name" --date --from-date --to-date --limit N
time update: --hours N --comment "..." --date --wp N --activity N
Projects (project)
| Command | Description |
|---|---|
project list | List all projects |
project get <id> | Get project |
project create --name "..." | Create project |
project update <id> [options] | Update project |
project delete <id> | Delete project |
project versions [--id N] | List versions |
project categories [--id N] | List categories |
project create: --name --identifier --description --parent N --public
Notifications (notif)
| Command | Description |
|---|---|
notif list [--unread] [--reason X] | List notifications |
notif count | Unread count |
notif read <id> | Mark as read |
notif unread <id> | Mark as unread |
notif read-all | Mark all read |
notif reasons: mentioned|assigned|watched|responsible|commented|created|prioritized|scheduled
Users (user)
| Command | Description |
|---|---|
user list | List all users |
user get <id> | Get user |
user me | Current user info |
Lookups (from config cache)
| Command | Description |
|---|---|
members | Project members (name + user_id) |
types | WP types (Task, Bug, etc.) |
statuses | All statuses |
priorities | All priorities |
Examples
# List open tasks assigned to Hung NB
uv run python op.py wp list --status open --assignee "Hung NB" --type Task
# Create a bug
uv run python op.py wp create --subject "Login page broken" --type Bug --priority High
# Update status and assignee
uv run python op.py wp update 675 --status "In progress" --assignee "Hung NB"
# Add comment to WP
uv run python op.py wp comment 675 --message "Fixed in commit abc123"
# Create a relation: WP 100 blocks WP 200
uv run python op.py wp add-relation 100 200 --type blocks
# Log 2.5 hours
uv run python op.py time log 675 --hours 2.5 --comment "Fixed login bug"
# Get today's logged time
uv run python op.py time today
# Time entries for date range
uv run python op.py time list --from-date 2026-02-01 --to-date 2026-02-28 --user "Tuan HV"
# Check unread notifications
uv run python op.py notif count
# List unread notifications
uv run python op.py notif list --unread --limit 10
# Get current user
uv run python op.py user me
# List project versions
uv run python op.py project versionsOutput Format
All commands output JSON. Example:
{"count": 2, "work_packages": [{"id": 675, "subject": "Login page", "type": "Bug", "status": "In progress"}]}⚠️ Important Notes
- Name resolution uses config cache. Refresh with:
uv run python op.py init <project_id> - Custom fields are project/type specific. Use
op.py configto see available ones. op.pyauto-loads.env— no manualload_dotenv()needed.
Advanced: Python API
For complex operations not covered by CLI, use Python packages directly:
uv run python -c "
from openproject_work_packages import list_work_packages
for wp in list_work_packages(project_id=13):
print(wp['subject'])
"Sub-package docs:
| Package | SKILL.md |
|---|---|
openproject_core | openproject-core/SKILL.md |
openproject_projects | openproject-projects/SKILL.md |
openproject_work_packages | openproject-work-packages/SKILL.md |
openproject_time | openproject-time/SKILL.md |
openproject_users | openproject-users/SKILL.md |
openproject_documents | openproject-documents/SKILL.md |
openproject_queries | openproject-queries/SKILL.md |
openproject_notifications | openproject-notifications/SKILL.md |
openproject_admin | openproject-admin/SKILL.md |
Python API Gotchas
list_*functions return generators → uselist(...)to convert- Time
hoursis ISO 8601 duration (e.g.,PT1H30M) → useparse_duration() update_work_package()auto-fetcheslockVersionif not providedget_schema()requires bothproject_idANDtype_id