agent-tool-openproject-cli 0.2.1__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agent_tool_openproject_cli-0.2.1/src/agent_tool_openproject_cli.egg-info → agent_tool_openproject_cli-0.3.0}/PKG-INFO +82 -16
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/README.md +81 -15
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/pyproject.toml +1 -1
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0/src/agent_tool_openproject_cli.egg-info}/PKG-INFO +82 -16
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/agent_tool_openproject_cli.egg-info/SOURCES.txt +3 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/__init__.py +1 -1
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/cli.py +61 -7
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/client.py +67 -15
- agent_tool_openproject_cli-0.3.0/src/opcli/commands/context.py +130 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/costs.py +63 -2
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/guide.py +26 -2
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/search.py +4 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/time_entries.py +8 -1
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/workpackages.py +4 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/config.py +8 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/context.py +5 -2
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/errors.py +12 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/output.py +61 -3
- agent_tool_openproject_cli-0.3.0/tests/test_client_retry.py +88 -0
- agent_tool_openproject_cli-0.3.0/tests/test_context.py +60 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_cost.py +19 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_output.py +32 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_output_render.py +44 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_time.py +6 -1
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/LICENSE +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/setup.cfg +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/agent_tool_openproject_cli.egg-info/dependency_links.txt +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/agent_tool_openproject_cli.egg-info/entry_points.txt +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/agent_tool_openproject_cli.egg-info/requires.txt +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/agent_tool_openproject_cli.egg-info/top_level.txt +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/__main__.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/__init__.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/_shared.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/attachments.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/auth.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/comments.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/custom_fields.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/filelinks.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/memberships.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/notifications.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/projects.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/raw.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/settings.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/users.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/wiki.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/credentials.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/duration.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/hal.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/resolve.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/searchspec.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/serialize.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/wpfilters.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_attachments.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_comments.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_config_unit.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_credentials_unit.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_guide_unit.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_members_users.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_misc.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_more_integration.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_projects.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_search.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_search_features.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_searchspec_unit.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_serialize_unit.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_unit.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_workpackages.py +0 -0
- {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_wpfilters_unit.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-tool-openproject-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Agent-ready command-line interface for OpenProject (work packages, projects, time, costs, search, and more). Installs the `openproject` command.
|
|
5
5
|
Author: Zierhut IT, Alexander Zierhut
|
|
6
6
|
License: MIT
|
|
@@ -31,19 +31,23 @@ Provides-Extra: build
|
|
|
31
31
|
Requires-Dist: pyinstaller>=6; extra == "build"
|
|
32
32
|
Dynamic: license-file
|
|
33
33
|
|
|
34
|
-
# openproject
|
|
34
|
+
# openproject — the agent-ready OpenProject CLI
|
|
35
35
|
|
|
36
36
|
> A fast, scriptable **OpenProject CLI** for managing work packages, projects,
|
|
37
37
|
> time tracking, comments, and per-person cost reports from your terminal — and
|
|
38
38
|
> the first OpenProject command-line tool designed to be driven by **AI agents**
|
|
39
39
|
> (Claude, Cursor, LLM tool-loops) as well as humans.
|
|
40
40
|
|
|
41
|
+
[](https://pypi.org/project/agent-tool-openproject-cli/)
|
|
41
42
|
[](https://github.com/alexander-zierhut/agent-tool-openproject-cli/actions/workflows/ci.yml)
|
|
42
|
-

|
|
43
44
|

|
|
44
45
|

|
|
45
46
|
|
|
46
|
-
`openproject-cli`
|
|
47
|
+
**Install:** `pipx install agent-tool-openproject-cli` — then run `openproject guide`.
|
|
48
|
+
|
|
49
|
+
[**agent-tool-openproject-cli**](https://pypi.org/project/agent-tool-openproject-cli/)
|
|
50
|
+
(the installed command is `openproject`) is a Python command-line interface for the
|
|
47
51
|
[OpenProject](https://www.openproject.org/) REST API v3. It covers the whole
|
|
48
52
|
day-to-day workflow — **work packages** (create/update/delete/move), assignees,
|
|
49
53
|
custom fields, projects, comments, **time entries**, a genuinely good
|
|
@@ -59,8 +63,13 @@ first-class JSON output so it slots straight into automation and AI agents.
|
|
|
59
63
|
- 🔎 **Discoverable search** — filter with plain flags (`--mine`, `--overdue`,
|
|
60
64
|
`--updated-since 7d`), one-word presets (`search mine`), or `--where` expressions.
|
|
61
65
|
Never memorise filter JSON: `search fields`, `search operators`, `search values`.
|
|
62
|
-
- 🖇️ **
|
|
63
|
-
command with `-f`, set a default,
|
|
66
|
+
- 🖇️ **Four output formats** — `json` (default), `table`, `markdown`, and `csv`;
|
|
67
|
+
pick per command with `-f`, set a default, select exact `--fields`, or `--stream`
|
|
68
|
+
NDJSON for big result sets.
|
|
69
|
+
- 🧪 **Safe by default** — `--dry-run` previews any write; the client retries
|
|
70
|
+
transient failures; and `openproject context` gives sticky per-session defaults.
|
|
71
|
+
- 💶 **Invoicing extras** — per-person cost reports, and a detailed CSV export that
|
|
72
|
+
includes **time-entry custom fields** (which OpenProject's own reports can't).
|
|
64
73
|
- 🔐 **Safe credentials** — API token stored in the OS keyring (Secret Service /
|
|
65
74
|
macOS Keychain / Windows Credential Locker), with a `0600` file fallback.
|
|
66
75
|
- 🧰 **Escape hatch** — `openproject raw` calls any endpoint the typed commands
|
|
@@ -74,6 +83,26 @@ first-class JSON output so it slots straight into automation and AI agents.
|
|
|
74
83
|
work package automation, time tracking CLI, project management CLI, AI agent tool,
|
|
75
84
|
LLM tooling, Claude, DevOps automation, invoicing.
|
|
76
85
|
|
|
86
|
+
## Compatibility
|
|
87
|
+
|
|
88
|
+
- **OpenProject API:** the stable REST **API v3** (HAL+JSON).
|
|
89
|
+
- **Tested against:** OpenProject **13, 14, 15, 16, and 17** (Community, all-in-one).
|
|
90
|
+
The full integration suite passes on every one — the CLI covers the whole feature
|
|
91
|
+
set across all five majors. (15.x also runs in CI on every push; the `compat`
|
|
92
|
+
workflow re-runs the 13–17 matrix.)
|
|
93
|
+
- **Version notes uncovered by testing:**
|
|
94
|
+
- **v16+** — time entries require a work package. Logging time against a *project*
|
|
95
|
+
only (`time add --project` with no `--work-package`) is rejected with
|
|
96
|
+
"Logged for can't be blank"; log against a work package instead.
|
|
97
|
+
- **v17** — the Docker image needs a real `SECRET_KEY_BASE` env var to boot
|
|
98
|
+
(a container-config change, unrelated to the CLI).
|
|
99
|
+
- The time-entry work-package **filter** name differs across versions
|
|
100
|
+
(`work_package_id` on ≤15 vs the newer `entity_*` form); the CLI detects and
|
|
101
|
+
adapts automatically.
|
|
102
|
+
- **Python:** 3.10+.
|
|
103
|
+
|
|
104
|
+
Anything version-specific can be reached directly with `openproject raw <method> <path>`.
|
|
105
|
+
|
|
77
106
|
---
|
|
78
107
|
|
|
79
108
|
## Quick start
|
|
@@ -82,16 +111,16 @@ LLM tooling, Claude, DevOps automation, invoicing.
|
|
|
82
111
|
|
|
83
112
|
Pick whichever fits your target:
|
|
84
113
|
|
|
85
|
-
**a) `pipx` (recommended — isolated, `openproject` on PATH, needs Python 3.10+)**
|
|
114
|
+
**a) `pipx` (recommended — isolated, puts `openproject` on your PATH, needs Python 3.10+)**
|
|
86
115
|
```bash
|
|
87
|
-
pipx install agent-tool-openproject-cli #
|
|
88
|
-
pipx install git+https://github.com/alexander-zierhut/agent-tool-openproject-cli.git # or straight from git
|
|
116
|
+
pipx install agent-tool-openproject-cli # installs the `openproject` command
|
|
89
117
|
```
|
|
90
118
|
|
|
91
|
-
**b) `pip
|
|
119
|
+
**b) `pip`**
|
|
92
120
|
```bash
|
|
93
|
-
|
|
94
|
-
|
|
121
|
+
pip install agent-tool-openproject-cli # from PyPI
|
|
122
|
+
# or, for local development from a clone:
|
|
123
|
+
python3 -m venv .venv && . .venv/bin/activate && pip install -e .
|
|
95
124
|
```
|
|
96
125
|
|
|
97
126
|
**c) Single self-contained binary (no Python on the target)**
|
|
@@ -305,6 +334,17 @@ openproject cost report --from 2026-07-01 --to 2026-07-31 --user jane.doe --rate
|
|
|
305
334
|
Without `--rates` it reports hours only. Most specific rate wins:
|
|
306
335
|
project+user → project default → user → global default.
|
|
307
336
|
|
|
337
|
+
**Detailed export with time-entry custom fields** — one row per entry, including
|
|
338
|
+
the time entry's custom fields (something OpenProject's own reports can't export).
|
|
339
|
+
Perfect for invoicing spreadsheets:
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
openproject cost report --month 2026-07 --rates rates.json --detailed -o csv > july.csv
|
|
343
|
+
# columns: date,user,project,workPackage,activity,hours,rate,amount,comment,Cost Center,Billable,…
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Set the custom fields when logging: `openproject time add 2 -w 42 --custom-fields '{"customField3":"CC-1000","customField4":true}'` (discover them with `openproject cf time`).
|
|
347
|
+
|
|
308
348
|
### Notifications — `openproject notify`
|
|
309
349
|
|
|
310
350
|
```bash
|
|
@@ -369,19 +409,45 @@ openproject auth logout --name prod
|
|
|
369
409
|
|
|
370
410
|
### Output format & settings — `openproject settings`
|
|
371
411
|
|
|
372
|
-
|
|
373
|
-
**`
|
|
374
|
-
docs/PRs). Choose per command, or set a persistent default.
|
|
412
|
+
Four output formats: **`json`** (default), **`table`**, **`markdown`**, and
|
|
413
|
+
**`csv`**. Choose per command, or set a persistent default.
|
|
375
414
|
|
|
376
415
|
```bash
|
|
377
416
|
openproject wp list -o table # global flag (before the command)
|
|
378
417
|
openproject wp list --format markdown # --format/-f works AFTER any command too
|
|
379
|
-
openproject wp
|
|
418
|
+
openproject search wp --mine -o csv # spreadsheet export
|
|
419
|
+
openproject wp get 42 --fields id,subject,assignee.name # pick exact fields (dotted ok)
|
|
380
420
|
|
|
381
421
|
openproject settings set-format table # persist a default
|
|
382
422
|
openproject settings show # current default, config path, profiles
|
|
383
423
|
```
|
|
384
424
|
|
|
425
|
+
**Preview & scale flags** (work anywhere on the line):
|
|
426
|
+
|
|
427
|
+
```bash
|
|
428
|
+
openproject wp update 42 --status Closed --dry-run # prints the request, sends nothing
|
|
429
|
+
openproject search wp --project big --stream # NDJSON, one object per line, as fetched
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
The client also **retries** transient failures (429 rate-limits + 5xx on reads)
|
|
433
|
+
with backoff, honoring `Retry-After`.
|
|
434
|
+
|
|
435
|
+
### Session context — `openproject context`
|
|
436
|
+
|
|
437
|
+
Stop repeating `--project`/`--user`/filters: set them once and they apply to
|
|
438
|
+
later commands as defaults (explicit flags always win; `--no-context` bypasses).
|
|
439
|
+
|
|
440
|
+
```bash
|
|
441
|
+
openproject context set --project webshop --assignee me
|
|
442
|
+
openproject context show # see the active defaults
|
|
443
|
+
openproject wp list # behaves like: wp list --project webshop --assignee me
|
|
444
|
+
openproject wp list --project other # explicit flag wins
|
|
445
|
+
openproject --no-context wp list # ignore the context for one command
|
|
446
|
+
|
|
447
|
+
openproject context save sprint # name it, switch between saved contexts
|
|
448
|
+
openproject context use sprint
|
|
449
|
+
```
|
|
450
|
+
|
|
385
451
|
On the **first interactive run** the CLI asks which default format you'd like and
|
|
386
452
|
saves your choice. Non-interactive runs (pipes, CI, agents) never prompt — they
|
|
387
453
|
default to `json`. Precedence: `--format`/`-f` → `-o/--output` → `$OPCLI_FORMAT`
|
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
# openproject
|
|
1
|
+
# openproject — the agent-ready OpenProject CLI
|
|
2
2
|
|
|
3
3
|
> A fast, scriptable **OpenProject CLI** for managing work packages, projects,
|
|
4
4
|
> time tracking, comments, and per-person cost reports from your terminal — and
|
|
5
5
|
> the first OpenProject command-line tool designed to be driven by **AI agents**
|
|
6
6
|
> (Claude, Cursor, LLM tool-loops) as well as humans.
|
|
7
7
|
|
|
8
|
+
[](https://pypi.org/project/agent-tool-openproject-cli/)
|
|
8
9
|
[](https://github.com/alexander-zierhut/agent-tool-openproject-cli/actions/workflows/ci.yml)
|
|
9
|
-

|
|
10
11
|

|
|
11
12
|

|
|
12
13
|
|
|
13
|
-
`openproject-cli`
|
|
14
|
+
**Install:** `pipx install agent-tool-openproject-cli` — then run `openproject guide`.
|
|
15
|
+
|
|
16
|
+
[**agent-tool-openproject-cli**](https://pypi.org/project/agent-tool-openproject-cli/)
|
|
17
|
+
(the installed command is `openproject`) is a Python command-line interface for the
|
|
14
18
|
[OpenProject](https://www.openproject.org/) REST API v3. It covers the whole
|
|
15
19
|
day-to-day workflow — **work packages** (create/update/delete/move), assignees,
|
|
16
20
|
custom fields, projects, comments, **time entries**, a genuinely good
|
|
@@ -26,8 +30,13 @@ first-class JSON output so it slots straight into automation and AI agents.
|
|
|
26
30
|
- 🔎 **Discoverable search** — filter with plain flags (`--mine`, `--overdue`,
|
|
27
31
|
`--updated-since 7d`), one-word presets (`search mine`), or `--where` expressions.
|
|
28
32
|
Never memorise filter JSON: `search fields`, `search operators`, `search values`.
|
|
29
|
-
- 🖇️ **
|
|
30
|
-
command with `-f`, set a default,
|
|
33
|
+
- 🖇️ **Four output formats** — `json` (default), `table`, `markdown`, and `csv`;
|
|
34
|
+
pick per command with `-f`, set a default, select exact `--fields`, or `--stream`
|
|
35
|
+
NDJSON for big result sets.
|
|
36
|
+
- 🧪 **Safe by default** — `--dry-run` previews any write; the client retries
|
|
37
|
+
transient failures; and `openproject context` gives sticky per-session defaults.
|
|
38
|
+
- 💶 **Invoicing extras** — per-person cost reports, and a detailed CSV export that
|
|
39
|
+
includes **time-entry custom fields** (which OpenProject's own reports can't).
|
|
31
40
|
- 🔐 **Safe credentials** — API token stored in the OS keyring (Secret Service /
|
|
32
41
|
macOS Keychain / Windows Credential Locker), with a `0600` file fallback.
|
|
33
42
|
- 🧰 **Escape hatch** — `openproject raw` calls any endpoint the typed commands
|
|
@@ -41,6 +50,26 @@ first-class JSON output so it slots straight into automation and AI agents.
|
|
|
41
50
|
work package automation, time tracking CLI, project management CLI, AI agent tool,
|
|
42
51
|
LLM tooling, Claude, DevOps automation, invoicing.
|
|
43
52
|
|
|
53
|
+
## Compatibility
|
|
54
|
+
|
|
55
|
+
- **OpenProject API:** the stable REST **API v3** (HAL+JSON).
|
|
56
|
+
- **Tested against:** OpenProject **13, 14, 15, 16, and 17** (Community, all-in-one).
|
|
57
|
+
The full integration suite passes on every one — the CLI covers the whole feature
|
|
58
|
+
set across all five majors. (15.x also runs in CI on every push; the `compat`
|
|
59
|
+
workflow re-runs the 13–17 matrix.)
|
|
60
|
+
- **Version notes uncovered by testing:**
|
|
61
|
+
- **v16+** — time entries require a work package. Logging time against a *project*
|
|
62
|
+
only (`time add --project` with no `--work-package`) is rejected with
|
|
63
|
+
"Logged for can't be blank"; log against a work package instead.
|
|
64
|
+
- **v17** — the Docker image needs a real `SECRET_KEY_BASE` env var to boot
|
|
65
|
+
(a container-config change, unrelated to the CLI).
|
|
66
|
+
- The time-entry work-package **filter** name differs across versions
|
|
67
|
+
(`work_package_id` on ≤15 vs the newer `entity_*` form); the CLI detects and
|
|
68
|
+
adapts automatically.
|
|
69
|
+
- **Python:** 3.10+.
|
|
70
|
+
|
|
71
|
+
Anything version-specific can be reached directly with `openproject raw <method> <path>`.
|
|
72
|
+
|
|
44
73
|
---
|
|
45
74
|
|
|
46
75
|
## Quick start
|
|
@@ -49,16 +78,16 @@ LLM tooling, Claude, DevOps automation, invoicing.
|
|
|
49
78
|
|
|
50
79
|
Pick whichever fits your target:
|
|
51
80
|
|
|
52
|
-
**a) `pipx` (recommended — isolated, `openproject` on PATH, needs Python 3.10+)**
|
|
81
|
+
**a) `pipx` (recommended — isolated, puts `openproject` on your PATH, needs Python 3.10+)**
|
|
53
82
|
```bash
|
|
54
|
-
pipx install agent-tool-openproject-cli #
|
|
55
|
-
pipx install git+https://github.com/alexander-zierhut/agent-tool-openproject-cli.git # or straight from git
|
|
83
|
+
pipx install agent-tool-openproject-cli # installs the `openproject` command
|
|
56
84
|
```
|
|
57
85
|
|
|
58
|
-
**b) `pip
|
|
86
|
+
**b) `pip`**
|
|
59
87
|
```bash
|
|
60
|
-
|
|
61
|
-
|
|
88
|
+
pip install agent-tool-openproject-cli # from PyPI
|
|
89
|
+
# or, for local development from a clone:
|
|
90
|
+
python3 -m venv .venv && . .venv/bin/activate && pip install -e .
|
|
62
91
|
```
|
|
63
92
|
|
|
64
93
|
**c) Single self-contained binary (no Python on the target)**
|
|
@@ -272,6 +301,17 @@ openproject cost report --from 2026-07-01 --to 2026-07-31 --user jane.doe --rate
|
|
|
272
301
|
Without `--rates` it reports hours only. Most specific rate wins:
|
|
273
302
|
project+user → project default → user → global default.
|
|
274
303
|
|
|
304
|
+
**Detailed export with time-entry custom fields** — one row per entry, including
|
|
305
|
+
the time entry's custom fields (something OpenProject's own reports can't export).
|
|
306
|
+
Perfect for invoicing spreadsheets:
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
openproject cost report --month 2026-07 --rates rates.json --detailed -o csv > july.csv
|
|
310
|
+
# columns: date,user,project,workPackage,activity,hours,rate,amount,comment,Cost Center,Billable,…
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
Set the custom fields when logging: `openproject time add 2 -w 42 --custom-fields '{"customField3":"CC-1000","customField4":true}'` (discover them with `openproject cf time`).
|
|
314
|
+
|
|
275
315
|
### Notifications — `openproject notify`
|
|
276
316
|
|
|
277
317
|
```bash
|
|
@@ -336,19 +376,45 @@ openproject auth logout --name prod
|
|
|
336
376
|
|
|
337
377
|
### Output format & settings — `openproject settings`
|
|
338
378
|
|
|
339
|
-
|
|
340
|
-
**`
|
|
341
|
-
docs/PRs). Choose per command, or set a persistent default.
|
|
379
|
+
Four output formats: **`json`** (default), **`table`**, **`markdown`**, and
|
|
380
|
+
**`csv`**. Choose per command, or set a persistent default.
|
|
342
381
|
|
|
343
382
|
```bash
|
|
344
383
|
openproject wp list -o table # global flag (before the command)
|
|
345
384
|
openproject wp list --format markdown # --format/-f works AFTER any command too
|
|
346
|
-
openproject wp
|
|
385
|
+
openproject search wp --mine -o csv # spreadsheet export
|
|
386
|
+
openproject wp get 42 --fields id,subject,assignee.name # pick exact fields (dotted ok)
|
|
347
387
|
|
|
348
388
|
openproject settings set-format table # persist a default
|
|
349
389
|
openproject settings show # current default, config path, profiles
|
|
350
390
|
```
|
|
351
391
|
|
|
392
|
+
**Preview & scale flags** (work anywhere on the line):
|
|
393
|
+
|
|
394
|
+
```bash
|
|
395
|
+
openproject wp update 42 --status Closed --dry-run # prints the request, sends nothing
|
|
396
|
+
openproject search wp --project big --stream # NDJSON, one object per line, as fetched
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
The client also **retries** transient failures (429 rate-limits + 5xx on reads)
|
|
400
|
+
with backoff, honoring `Retry-After`.
|
|
401
|
+
|
|
402
|
+
### Session context — `openproject context`
|
|
403
|
+
|
|
404
|
+
Stop repeating `--project`/`--user`/filters: set them once and they apply to
|
|
405
|
+
later commands as defaults (explicit flags always win; `--no-context` bypasses).
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
openproject context set --project webshop --assignee me
|
|
409
|
+
openproject context show # see the active defaults
|
|
410
|
+
openproject wp list # behaves like: wp list --project webshop --assignee me
|
|
411
|
+
openproject wp list --project other # explicit flag wins
|
|
412
|
+
openproject --no-context wp list # ignore the context for one command
|
|
413
|
+
|
|
414
|
+
openproject context save sprint # name it, switch between saved contexts
|
|
415
|
+
openproject context use sprint
|
|
416
|
+
```
|
|
417
|
+
|
|
352
418
|
On the **first interactive run** the CLI asks which default format you'd like and
|
|
353
419
|
saves your choice. Non-interactive runs (pipes, CI, agents) never prompt — they
|
|
354
420
|
default to `json`. Precedence: `--format`/`-f` → `-o/--output` → `$OPCLI_FORMAT`
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "agent-tool-openproject-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
description = "Agent-ready command-line interface for OpenProject (work packages, projects, time, costs, search, and more). Installs the `openproject` command."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-tool-openproject-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Agent-ready command-line interface for OpenProject (work packages, projects, time, costs, search, and more). Installs the `openproject` command.
|
|
5
5
|
Author: Zierhut IT, Alexander Zierhut
|
|
6
6
|
License: MIT
|
|
@@ -31,19 +31,23 @@ Provides-Extra: build
|
|
|
31
31
|
Requires-Dist: pyinstaller>=6; extra == "build"
|
|
32
32
|
Dynamic: license-file
|
|
33
33
|
|
|
34
|
-
# openproject
|
|
34
|
+
# openproject — the agent-ready OpenProject CLI
|
|
35
35
|
|
|
36
36
|
> A fast, scriptable **OpenProject CLI** for managing work packages, projects,
|
|
37
37
|
> time tracking, comments, and per-person cost reports from your terminal — and
|
|
38
38
|
> the first OpenProject command-line tool designed to be driven by **AI agents**
|
|
39
39
|
> (Claude, Cursor, LLM tool-loops) as well as humans.
|
|
40
40
|
|
|
41
|
+
[](https://pypi.org/project/agent-tool-openproject-cli/)
|
|
41
42
|
[](https://github.com/alexander-zierhut/agent-tool-openproject-cli/actions/workflows/ci.yml)
|
|
42
|
-

|
|
43
44
|

|
|
44
45
|

|
|
45
46
|
|
|
46
|
-
`openproject-cli`
|
|
47
|
+
**Install:** `pipx install agent-tool-openproject-cli` — then run `openproject guide`.
|
|
48
|
+
|
|
49
|
+
[**agent-tool-openproject-cli**](https://pypi.org/project/agent-tool-openproject-cli/)
|
|
50
|
+
(the installed command is `openproject`) is a Python command-line interface for the
|
|
47
51
|
[OpenProject](https://www.openproject.org/) REST API v3. It covers the whole
|
|
48
52
|
day-to-day workflow — **work packages** (create/update/delete/move), assignees,
|
|
49
53
|
custom fields, projects, comments, **time entries**, a genuinely good
|
|
@@ -59,8 +63,13 @@ first-class JSON output so it slots straight into automation and AI agents.
|
|
|
59
63
|
- 🔎 **Discoverable search** — filter with plain flags (`--mine`, `--overdue`,
|
|
60
64
|
`--updated-since 7d`), one-word presets (`search mine`), or `--where` expressions.
|
|
61
65
|
Never memorise filter JSON: `search fields`, `search operators`, `search values`.
|
|
62
|
-
- 🖇️ **
|
|
63
|
-
command with `-f`, set a default,
|
|
66
|
+
- 🖇️ **Four output formats** — `json` (default), `table`, `markdown`, and `csv`;
|
|
67
|
+
pick per command with `-f`, set a default, select exact `--fields`, or `--stream`
|
|
68
|
+
NDJSON for big result sets.
|
|
69
|
+
- 🧪 **Safe by default** — `--dry-run` previews any write; the client retries
|
|
70
|
+
transient failures; and `openproject context` gives sticky per-session defaults.
|
|
71
|
+
- 💶 **Invoicing extras** — per-person cost reports, and a detailed CSV export that
|
|
72
|
+
includes **time-entry custom fields** (which OpenProject's own reports can't).
|
|
64
73
|
- 🔐 **Safe credentials** — API token stored in the OS keyring (Secret Service /
|
|
65
74
|
macOS Keychain / Windows Credential Locker), with a `0600` file fallback.
|
|
66
75
|
- 🧰 **Escape hatch** — `openproject raw` calls any endpoint the typed commands
|
|
@@ -74,6 +83,26 @@ first-class JSON output so it slots straight into automation and AI agents.
|
|
|
74
83
|
work package automation, time tracking CLI, project management CLI, AI agent tool,
|
|
75
84
|
LLM tooling, Claude, DevOps automation, invoicing.
|
|
76
85
|
|
|
86
|
+
## Compatibility
|
|
87
|
+
|
|
88
|
+
- **OpenProject API:** the stable REST **API v3** (HAL+JSON).
|
|
89
|
+
- **Tested against:** OpenProject **13, 14, 15, 16, and 17** (Community, all-in-one).
|
|
90
|
+
The full integration suite passes on every one — the CLI covers the whole feature
|
|
91
|
+
set across all five majors. (15.x also runs in CI on every push; the `compat`
|
|
92
|
+
workflow re-runs the 13–17 matrix.)
|
|
93
|
+
- **Version notes uncovered by testing:**
|
|
94
|
+
- **v16+** — time entries require a work package. Logging time against a *project*
|
|
95
|
+
only (`time add --project` with no `--work-package`) is rejected with
|
|
96
|
+
"Logged for can't be blank"; log against a work package instead.
|
|
97
|
+
- **v17** — the Docker image needs a real `SECRET_KEY_BASE` env var to boot
|
|
98
|
+
(a container-config change, unrelated to the CLI).
|
|
99
|
+
- The time-entry work-package **filter** name differs across versions
|
|
100
|
+
(`work_package_id` on ≤15 vs the newer `entity_*` form); the CLI detects and
|
|
101
|
+
adapts automatically.
|
|
102
|
+
- **Python:** 3.10+.
|
|
103
|
+
|
|
104
|
+
Anything version-specific can be reached directly with `openproject raw <method> <path>`.
|
|
105
|
+
|
|
77
106
|
---
|
|
78
107
|
|
|
79
108
|
## Quick start
|
|
@@ -82,16 +111,16 @@ LLM tooling, Claude, DevOps automation, invoicing.
|
|
|
82
111
|
|
|
83
112
|
Pick whichever fits your target:
|
|
84
113
|
|
|
85
|
-
**a) `pipx` (recommended — isolated, `openproject` on PATH, needs Python 3.10+)**
|
|
114
|
+
**a) `pipx` (recommended — isolated, puts `openproject` on your PATH, needs Python 3.10+)**
|
|
86
115
|
```bash
|
|
87
|
-
pipx install agent-tool-openproject-cli #
|
|
88
|
-
pipx install git+https://github.com/alexander-zierhut/agent-tool-openproject-cli.git # or straight from git
|
|
116
|
+
pipx install agent-tool-openproject-cli # installs the `openproject` command
|
|
89
117
|
```
|
|
90
118
|
|
|
91
|
-
**b) `pip
|
|
119
|
+
**b) `pip`**
|
|
92
120
|
```bash
|
|
93
|
-
|
|
94
|
-
|
|
121
|
+
pip install agent-tool-openproject-cli # from PyPI
|
|
122
|
+
# or, for local development from a clone:
|
|
123
|
+
python3 -m venv .venv && . .venv/bin/activate && pip install -e .
|
|
95
124
|
```
|
|
96
125
|
|
|
97
126
|
**c) Single self-contained binary (no Python on the target)**
|
|
@@ -305,6 +334,17 @@ openproject cost report --from 2026-07-01 --to 2026-07-31 --user jane.doe --rate
|
|
|
305
334
|
Without `--rates` it reports hours only. Most specific rate wins:
|
|
306
335
|
project+user → project default → user → global default.
|
|
307
336
|
|
|
337
|
+
**Detailed export with time-entry custom fields** — one row per entry, including
|
|
338
|
+
the time entry's custom fields (something OpenProject's own reports can't export).
|
|
339
|
+
Perfect for invoicing spreadsheets:
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
openproject cost report --month 2026-07 --rates rates.json --detailed -o csv > july.csv
|
|
343
|
+
# columns: date,user,project,workPackage,activity,hours,rate,amount,comment,Cost Center,Billable,…
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Set the custom fields when logging: `openproject time add 2 -w 42 --custom-fields '{"customField3":"CC-1000","customField4":true}'` (discover them with `openproject cf time`).
|
|
347
|
+
|
|
308
348
|
### Notifications — `openproject notify`
|
|
309
349
|
|
|
310
350
|
```bash
|
|
@@ -369,19 +409,45 @@ openproject auth logout --name prod
|
|
|
369
409
|
|
|
370
410
|
### Output format & settings — `openproject settings`
|
|
371
411
|
|
|
372
|
-
|
|
373
|
-
**`
|
|
374
|
-
docs/PRs). Choose per command, or set a persistent default.
|
|
412
|
+
Four output formats: **`json`** (default), **`table`**, **`markdown`**, and
|
|
413
|
+
**`csv`**. Choose per command, or set a persistent default.
|
|
375
414
|
|
|
376
415
|
```bash
|
|
377
416
|
openproject wp list -o table # global flag (before the command)
|
|
378
417
|
openproject wp list --format markdown # --format/-f works AFTER any command too
|
|
379
|
-
openproject wp
|
|
418
|
+
openproject search wp --mine -o csv # spreadsheet export
|
|
419
|
+
openproject wp get 42 --fields id,subject,assignee.name # pick exact fields (dotted ok)
|
|
380
420
|
|
|
381
421
|
openproject settings set-format table # persist a default
|
|
382
422
|
openproject settings show # current default, config path, profiles
|
|
383
423
|
```
|
|
384
424
|
|
|
425
|
+
**Preview & scale flags** (work anywhere on the line):
|
|
426
|
+
|
|
427
|
+
```bash
|
|
428
|
+
openproject wp update 42 --status Closed --dry-run # prints the request, sends nothing
|
|
429
|
+
openproject search wp --project big --stream # NDJSON, one object per line, as fetched
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
The client also **retries** transient failures (429 rate-limits + 5xx on reads)
|
|
433
|
+
with backoff, honoring `Retry-After`.
|
|
434
|
+
|
|
435
|
+
### Session context — `openproject context`
|
|
436
|
+
|
|
437
|
+
Stop repeating `--project`/`--user`/filters: set them once and they apply to
|
|
438
|
+
later commands as defaults (explicit flags always win; `--no-context` bypasses).
|
|
439
|
+
|
|
440
|
+
```bash
|
|
441
|
+
openproject context set --project webshop --assignee me
|
|
442
|
+
openproject context show # see the active defaults
|
|
443
|
+
openproject wp list # behaves like: wp list --project webshop --assignee me
|
|
444
|
+
openproject wp list --project other # explicit flag wins
|
|
445
|
+
openproject --no-context wp list # ignore the context for one command
|
|
446
|
+
|
|
447
|
+
openproject context save sprint # name it, switch between saved contexts
|
|
448
|
+
openproject context use sprint
|
|
449
|
+
```
|
|
450
|
+
|
|
385
451
|
On the **first interactive run** the CLI asks which default format you'd like and
|
|
386
452
|
saves your choice. Non-interactive runs (pipes, CI, agents) never prompt — they
|
|
387
453
|
default to `json`. Precedence: `--format`/`-f` → `-o/--output` → `$OPCLI_FORMAT`
|
|
@@ -27,6 +27,7 @@ src/opcli/commands/_shared.py
|
|
|
27
27
|
src/opcli/commands/attachments.py
|
|
28
28
|
src/opcli/commands/auth.py
|
|
29
29
|
src/opcli/commands/comments.py
|
|
30
|
+
src/opcli/commands/context.py
|
|
30
31
|
src/opcli/commands/costs.py
|
|
31
32
|
src/opcli/commands/custom_fields.py
|
|
32
33
|
src/opcli/commands/filelinks.py
|
|
@@ -42,8 +43,10 @@ src/opcli/commands/users.py
|
|
|
42
43
|
src/opcli/commands/wiki.py
|
|
43
44
|
src/opcli/commands/workpackages.py
|
|
44
45
|
tests/test_attachments.py
|
|
46
|
+
tests/test_client_retry.py
|
|
45
47
|
tests/test_comments.py
|
|
46
48
|
tests/test_config_unit.py
|
|
49
|
+
tests/test_context.py
|
|
47
50
|
tests/test_cost.py
|
|
48
51
|
tests/test_credentials_unit.py
|
|
49
52
|
tests/test_guide_unit.py
|