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.
Files changed (68) hide show
  1. {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
  2. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/README.md +81 -15
  3. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/pyproject.toml +1 -1
  4. {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
  5. {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
  6. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/__init__.py +1 -1
  7. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/cli.py +61 -7
  8. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/client.py +67 -15
  9. agent_tool_openproject_cli-0.3.0/src/opcli/commands/context.py +130 -0
  10. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/costs.py +63 -2
  11. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/guide.py +26 -2
  12. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/search.py +4 -0
  13. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/time_entries.py +8 -1
  14. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/workpackages.py +4 -0
  15. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/config.py +8 -0
  16. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/context.py +5 -2
  17. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/errors.py +12 -0
  18. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/output.py +61 -3
  19. agent_tool_openproject_cli-0.3.0/tests/test_client_retry.py +88 -0
  20. agent_tool_openproject_cli-0.3.0/tests/test_context.py +60 -0
  21. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_cost.py +19 -0
  22. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_output.py +32 -0
  23. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_output_render.py +44 -0
  24. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_time.py +6 -1
  25. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/LICENSE +0 -0
  26. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/setup.cfg +0 -0
  27. {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
  28. {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
  29. {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
  30. {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
  31. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/__main__.py +0 -0
  32. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/__init__.py +0 -0
  33. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/_shared.py +0 -0
  34. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/attachments.py +0 -0
  35. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/auth.py +0 -0
  36. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/comments.py +0 -0
  37. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/custom_fields.py +0 -0
  38. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/filelinks.py +0 -0
  39. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/memberships.py +0 -0
  40. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/notifications.py +0 -0
  41. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/projects.py +0 -0
  42. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/raw.py +0 -0
  43. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/settings.py +0 -0
  44. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/users.py +0 -0
  45. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/commands/wiki.py +0 -0
  46. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/credentials.py +0 -0
  47. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/duration.py +0 -0
  48. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/hal.py +0 -0
  49. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/resolve.py +0 -0
  50. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/searchspec.py +0 -0
  51. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/serialize.py +0 -0
  52. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/src/opcli/wpfilters.py +0 -0
  53. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_attachments.py +0 -0
  54. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_comments.py +0 -0
  55. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_config_unit.py +0 -0
  56. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_credentials_unit.py +0 -0
  57. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_guide_unit.py +0 -0
  58. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_members_users.py +0 -0
  59. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_misc.py +0 -0
  60. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_more_integration.py +0 -0
  61. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_projects.py +0 -0
  62. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_search.py +0 -0
  63. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_search_features.py +0 -0
  64. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_searchspec_unit.py +0 -0
  65. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_serialize_unit.py +0 -0
  66. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_unit.py +0 -0
  67. {agent_tool_openproject_cli-0.2.1 → agent_tool_openproject_cli-0.3.0}/tests/test_workpackages.py +0 -0
  68. {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.2.1
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-cli — the agent-ready OpenProject command-line interface
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
+ [![PyPI](https://img.shields.io/pypi/v/agent-tool-openproject-cli)](https://pypi.org/project/agent-tool-openproject-cli/)
41
42
  [![CI](https://github.com/alexander-zierhut/agent-tool-openproject-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/alexander-zierhut/agent-tool-openproject-cli/actions/workflows/ci.yml)
42
- ![Python](https://img.shields.io/badge/python-3.10%2B-blue)
43
+ ![Python](https://img.shields.io/pypi/pyversions/agent-tool-openproject-cli)
43
44
  ![License: MIT](https://img.shields.io/badge/license-MIT-green)
44
45
  ![Agent ready](https://img.shields.io/badge/agent-ready-8A2BE2)
45
46
 
46
- `openproject-cli` is a Python command-line interface for the
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
- - 🖇️ **Three output formats** — `json` (default), `table`, and `markdown`; pick per
63
- command with `-f`, set a default, or select exact `--fields`.
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 # from PyPI (installs the `openproject` command)
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` into a venv (for development)**
119
+ **b) `pip`**
92
120
  ```bash
93
- python3 -m venv .venv && . .venv/bin/activate
94
- pip install -e . # from this directory
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
- Three output formats: **`json`** (default, best for scripts/agents),
373
- **`table`** (human-readable terminal tables), and **`markdown`** (paste into
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 get 42 -f md
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-cli — the agent-ready OpenProject command-line interface
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
+ [![PyPI](https://img.shields.io/pypi/v/agent-tool-openproject-cli)](https://pypi.org/project/agent-tool-openproject-cli/)
8
9
  [![CI](https://github.com/alexander-zierhut/agent-tool-openproject-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/alexander-zierhut/agent-tool-openproject-cli/actions/workflows/ci.yml)
9
- ![Python](https://img.shields.io/badge/python-3.10%2B-blue)
10
+ ![Python](https://img.shields.io/pypi/pyversions/agent-tool-openproject-cli)
10
11
  ![License: MIT](https://img.shields.io/badge/license-MIT-green)
11
12
  ![Agent ready](https://img.shields.io/badge/agent-ready-8A2BE2)
12
13
 
13
- `openproject-cli` is a Python command-line interface for the
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
- - 🖇️ **Three output formats** — `json` (default), `table`, and `markdown`; pick per
30
- command with `-f`, set a default, or select exact `--fields`.
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 # from PyPI (installs the `openproject` command)
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` into a venv (for development)**
86
+ **b) `pip`**
59
87
  ```bash
60
- python3 -m venv .venv && . .venv/bin/activate
61
- pip install -e . # from this directory
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
- Three output formats: **`json`** (default, best for scripts/agents),
340
- **`table`** (human-readable terminal tables), and **`markdown`** (paste into
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 get 42 -f md
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.2.1"
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.2.1
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-cli — the agent-ready OpenProject command-line interface
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
+ [![PyPI](https://img.shields.io/pypi/v/agent-tool-openproject-cli)](https://pypi.org/project/agent-tool-openproject-cli/)
41
42
  [![CI](https://github.com/alexander-zierhut/agent-tool-openproject-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/alexander-zierhut/agent-tool-openproject-cli/actions/workflows/ci.yml)
42
- ![Python](https://img.shields.io/badge/python-3.10%2B-blue)
43
+ ![Python](https://img.shields.io/pypi/pyversions/agent-tool-openproject-cli)
43
44
  ![License: MIT](https://img.shields.io/badge/license-MIT-green)
44
45
  ![Agent ready](https://img.shields.io/badge/agent-ready-8A2BE2)
45
46
 
46
- `openproject-cli` is a Python command-line interface for the
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
- - 🖇️ **Three output formats** — `json` (default), `table`, and `markdown`; pick per
63
- command with `-f`, set a default, or select exact `--fields`.
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 # from PyPI (installs the `openproject` command)
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` into a venv (for development)**
119
+ **b) `pip`**
92
120
  ```bash
93
- python3 -m venv .venv && . .venv/bin/activate
94
- pip install -e . # from this directory
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
- Three output formats: **`json`** (default, best for scripts/agents),
373
- **`table`** (human-readable terminal tables), and **`markdown`** (paste into
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 get 42 -f md
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
@@ -1,3 +1,3 @@
1
1
  """opcli — an agent-friendly command-line interface for OpenProject."""
2
2
 
3
- __version__ = "0.2.1"
3
+ __version__ = "0.3.0"