clockify-unofficial-cli 0.2.0__tar.gz → 1.0.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 (191) hide show
  1. clockify_unofficial_cli-1.0.0/.claude-plugin/marketplace.json +15 -0
  2. clockify_unofficial_cli-1.0.0/.claude-plugin/plugin.json +18 -0
  3. clockify_unofficial_cli-1.0.0/AGENTS.md +117 -0
  4. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/Makefile +1 -1
  5. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/PKG-INFO +64 -21
  6. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/README.md +61 -18
  7. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/docs/ARCHITECTURE.md +51 -26
  8. clockify_unofficial_cli-1.0.0/docs/ROADMAP.md +137 -0
  9. clockify_unofficial_cli-1.0.0/docs/agents/agent-skills-format.md +68 -0
  10. clockify_unofficial_cli-1.0.0/docs/agents/claude-code-install-scopes.md +50 -0
  11. clockify_unofficial_cli-1.0.0/docs/agents/claude-code-marketplace-install.md +67 -0
  12. clockify_unofficial_cli-1.0.0/docs/agents/claude-code-plugin-updates.md +71 -0
  13. clockify_unofficial_cli-1.0.0/docs/agents/claude-code-shell-install.md +58 -0
  14. clockify_unofficial_cli-1.0.0/docs/agents/claude-code-skill-invocation.md +52 -0
  15. clockify_unofficial_cli-1.0.0/docs/agents/index.md +50 -0
  16. clockify_unofficial_cli-1.0.0/docs/agents/install-channels.md +65 -0
  17. clockify_unofficial_cli-1.0.0/docs/agents/npx-skills-install.md +65 -0
  18. clockify_unofficial_cli-1.0.0/docs/agents/npx-skills-manage.md +58 -0
  19. clockify_unofficial_cli-1.0.0/docs/agents/opencode-mcp-config.md +83 -0
  20. clockify_unofficial_cli-1.0.0/docs/agents/opencode-skill-discovery.md +50 -0
  21. clockify_unofficial_cli-1.0.0/docs/agents/opencode-skill-permissions.md +48 -0
  22. clockify_unofficial_cli-1.0.0/docs/agents/plugin-identity.md +74 -0
  23. clockify_unofficial_cli-1.0.0/docs/commands/expenses.md +47 -0
  24. clockify_unofficial_cli-1.0.0/docs/commands/index.md +11 -0
  25. clockify_unofficial_cli-1.0.0/docs/commands/invoices.md +68 -0
  26. clockify_unofficial_cli-1.0.0/docs/commands/reports.md +38 -0
  27. clockify_unofficial_cli-1.0.0/docs/commands/time-off.md +52 -0
  28. clockify_unofficial_cli-1.0.0/docs/coverage.md +285 -0
  29. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/docs/index.md +12 -1
  30. clockify_unofficial_cli-1.0.0/docs/log.md +81 -0
  31. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/docs/python/typer-parameter-objects.md +4 -0
  32. clockify_unofficial_cli-1.0.0/docs/toolchain/claude-plugin.md +22 -0
  33. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/docs/toolchain/index.md +2 -0
  34. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/docs/toolchain/layering-rule.md +1 -0
  35. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/docs/toolchain/releasing.md +4 -2
  36. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/mk/python.mk +2 -2
  37. clockify_unofficial_cli-1.0.0/mk/skills.mk +10 -0
  38. clockify_unofficial_cli-1.0.0/package.json +8 -0
  39. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/pyproject.toml +12 -30
  40. clockify_unofficial_cli-1.0.0/skills/clockify-cli/SKILL.md +94 -0
  41. clockify_unofficial_cli-1.0.0/skills/clockify-cli/references/exit-codes.md +17 -0
  42. clockify_unofficial_cli-1.0.0/skills/clockify-time-tracking/SKILL.md +66 -0
  43. clockify_unofficial_cli-1.0.0/skills/clockify-time-tracking/references/exit-codes.md +17 -0
  44. clockify_unofficial_cli-1.0.0/src/clockify_unofficial_cli/commands/approval.py +174 -0
  45. clockify_unofficial_cli-1.0.0/src/clockify_unofficial_cli/commands/expense.py +197 -0
  46. clockify_unofficial_cli-1.0.0/src/clockify_unofficial_cli/commands/expense_category.py +141 -0
  47. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/commands/group.py +29 -0
  48. clockify_unofficial_cli-1.0.0/src/clockify_unofficial_cli/commands/invoice.py +257 -0
  49. clockify_unofficial_cli-1.0.0/src/clockify_unofficial_cli/commands/invoice_item.py +115 -0
  50. clockify_unofficial_cli-1.0.0/src/clockify_unofficial_cli/commands/invoice_payment.py +87 -0
  51. clockify_unofficial_cli-1.0.0/src/clockify_unofficial_cli/commands/invoice_settings.py +72 -0
  52. clockify_unofficial_cli-1.0.0/src/clockify_unofficial_cli/commands/report.py +158 -0
  53. clockify_unofficial_cli-1.0.0/src/clockify_unofficial_cli/commands/shared_report.py +50 -0
  54. clockify_unofficial_cli-1.0.0/src/clockify_unofficial_cli/commands/time_off.py +12 -0
  55. clockify_unofficial_cli-1.0.0/src/clockify_unofficial_cli/commands/time_off_balance.py +214 -0
  56. clockify_unofficial_cli-1.0.0/src/clockify_unofficial_cli/commands/time_off_policy.py +239 -0
  57. clockify_unofficial_cli-1.0.0/src/clockify_unofficial_cli/commands/time_off_request.py +172 -0
  58. clockify_unofficial_cli-1.0.0/src/clockify_unofficial_cli/commands/webhook.py +240 -0
  59. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/main.py +14 -0
  60. clockify_unofficial_cli-1.0.0/src/clockify_unofficial_cli/output/columns.py +281 -0
  61. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/runtime/params.py +17 -1
  62. clockify_unofficial_cli-1.0.0/src/clockify_unofficial_cli/services/expenses.py +101 -0
  63. clockify_unofficial_cli-1.0.0/src/clockify_unofficial_cli/services/files.py +36 -0
  64. clockify_unofficial_cli-1.0.0/src/clockify_unofficial_cli/services/invoices.py +164 -0
  65. clockify_unofficial_cli-1.0.0/src/clockify_unofficial_cli/services/money.py +15 -0
  66. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/services/parsing.py +5 -1
  67. clockify_unofficial_cli-1.0.0/src/clockify_unofficial_cli/services/reports.py +193 -0
  68. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/services/resolve.py +28 -0
  69. clockify_unofficial_cli-1.0.0/src/clockify_unofficial_cli/services/time_off.py +121 -0
  70. clockify_unofficial_cli-1.0.0/tests/unit/commands/test_approval.py +137 -0
  71. clockify_unofficial_cli-1.0.0/tests/unit/commands/test_expense.py +459 -0
  72. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/commands/test_group.py +51 -0
  73. clockify_unofficial_cli-1.0.0/tests/unit/commands/test_invoice.py +536 -0
  74. clockify_unofficial_cli-1.0.0/tests/unit/commands/test_report.py +129 -0
  75. clockify_unofficial_cli-1.0.0/tests/unit/commands/test_time_off.py +469 -0
  76. clockify_unofficial_cli-1.0.0/tests/unit/commands/test_webhook.py +236 -0
  77. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/runtime/test_params.py +37 -0
  78. clockify_unofficial_cli-1.0.0/tests/unit/services/test_expenses.py +33 -0
  79. clockify_unofficial_cli-1.0.0/tests/unit/services/test_files.py +39 -0
  80. clockify_unofficial_cli-1.0.0/tests/unit/services/test_money.py +18 -0
  81. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/services/test_parsing.py +10 -0
  82. clockify_unofficial_cli-1.0.0/tests/unit/services/test_reports.py +136 -0
  83. clockify_unofficial_cli-1.0.0/tests/unit/services/test_time_off.py +100 -0
  84. clockify_unofficial_cli-1.0.0/tests/unit/test_plugin_manifest.py +56 -0
  85. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/uv.lock +73 -12
  86. clockify_unofficial_cli-0.2.0/AGENTS.md +0 -168
  87. clockify_unofficial_cli-0.2.0/docs/ROADMAP.md +0 -205
  88. clockify_unofficial_cli-0.2.0/docs/coverage.md +0 -280
  89. clockify_unofficial_cli-0.2.0/docs/log.md +0 -21
  90. clockify_unofficial_cli-0.2.0/package.json +0 -8
  91. clockify_unofficial_cli-0.2.0/src/clockify_unofficial_cli/output/columns.py +0 -88
  92. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/.editorconfig +0 -0
  93. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/.github/dependabot.yml +0 -0
  94. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/.github/workflows/ci.yml +0 -0
  95. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/.github/workflows/publish.yml +0 -0
  96. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/.github/workflows/python.yml +0 -0
  97. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/.gitignore +0 -0
  98. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/.markdownlint-cli2.jsonc +0 -0
  99. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/.markdownlint.yaml +0 -0
  100. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/.pre-commit-config.yaml +0 -0
  101. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/CONTRIBUTING.md +0 -0
  102. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/LICENSE +0 -0
  103. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/checkmake.ini +0 -0
  104. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/cspell.config.yaml +0 -0
  105. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/dictionary.txt +0 -0
  106. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/docs/conventions/commits-check.md +0 -0
  107. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/docs/conventions/index.md +0 -0
  108. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/docs/python/index.md +0 -0
  109. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/docs/python/interpreter-source.md +0 -0
  110. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/docs/python/pyproject-defaults.md +0 -0
  111. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/docs/toolchain/rejected-install-backends.md +0 -0
  112. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/mise.toml +0 -0
  113. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/mk/.gitkeep +0 -0
  114. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/mk/clockify.mk +0 -0
  115. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/pnpm-lock.yaml +0 -0
  116. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/pnpm-workspace.yaml +0 -0
  117. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/scripts/__init__.py +0 -0
  118. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/scripts/coverage_report.py +0 -0
  119. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/__init__.py +0 -0
  120. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/__main__.py +0 -0
  121. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/_version.py +0 -0
  122. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/auth/__init__.py +0 -0
  123. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/auth/credentials.py +0 -0
  124. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/auth/env_store.py +0 -0
  125. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/auth/file_store.py +0 -0
  126. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/auth/keyring_store.py +0 -0
  127. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/auth/resolver.py +0 -0
  128. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/commands/__init__.py +0 -0
  129. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/commands/auth.py +0 -0
  130. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/commands/client.py +0 -0
  131. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/commands/config.py +0 -0
  132. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/commands/custom_field.py +0 -0
  133. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/commands/entry.py +0 -0
  134. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/commands/project.py +0 -0
  135. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/commands/tag.py +0 -0
  136. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/commands/task.py +0 -0
  137. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/commands/timer_shortcuts.py +0 -0
  138. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/commands/user.py +0 -0
  139. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/commands/workspace.py +0 -0
  140. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/config/__init__.py +0 -0
  141. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/config/paths.py +0 -0
  142. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/config/settings.py +0 -0
  143. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/config/store.py +0 -0
  144. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/output/__init__.py +0 -0
  145. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/output/formats.py +0 -0
  146. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/output/registry.py +0 -0
  147. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/output/renderer.py +0 -0
  148. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/runtime/__init__.py +0 -0
  149. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/runtime/client_factory.py +0 -0
  150. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/runtime/context.py +0 -0
  151. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/runtime/errors.py +0 -0
  152. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/runtime/exit_codes.py +0 -0
  153. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/runtime/prompts.py +0 -0
  154. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/services/__init__.py +0 -0
  155. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/services/listing.py +0 -0
  156. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/src/clockify_unofficial_cli/services/timer.py +0 -0
  157. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/__init__.py +0 -0
  158. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/conftest.py +0 -0
  159. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/live/__init__.py +0 -0
  160. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/live/test_smoke.py +0 -0
  161. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/__init__.py +0 -0
  162. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/auth/__init__.py +0 -0
  163. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/auth/test_credentials.py +0 -0
  164. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/auth/test_stores.py +0 -0
  165. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/commands/__init__.py +0 -0
  166. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/commands/test_auth.py +0 -0
  167. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/commands/test_client.py +0 -0
  168. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/commands/test_config.py +0 -0
  169. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/commands/test_custom_field.py +0 -0
  170. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/commands/test_entry.py +0 -0
  171. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/commands/test_project.py +0 -0
  172. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/commands/test_tag.py +0 -0
  173. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/commands/test_task.py +0 -0
  174. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/commands/test_user.py +0 -0
  175. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/commands/test_workspace.py +0 -0
  176. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/config/__init__.py +0 -0
  177. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/config/test_settings.py +0 -0
  178. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/config/test_store.py +0 -0
  179. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/output/__init__.py +0 -0
  180. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/output/test_formats.py +0 -0
  181. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/runtime/__init__.py +0 -0
  182. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/runtime/test_context.py +0 -0
  183. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/runtime/test_errors.py +0 -0
  184. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/runtime/test_prompts.py +0 -0
  185. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/scripts/__init__.py +0 -0
  186. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/scripts/test_coverage_report.py +0 -0
  187. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/services/__init__.py +0 -0
  188. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/services/test_listing.py +0 -0
  189. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/services/test_resolve.py +0 -0
  190. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/services/test_timer.py +0 -0
  191. {clockify_unofficial_cli-0.2.0 → clockify_unofficial_cli-1.0.0}/tests/unit/test_main.py +0 -0
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "clockify-cli-skills",
3
+ "description": "Clockify time-tracking and workspace skills for Claude Code",
4
+ "owner": {
5
+ "name": "G.A.JAGUAR",
6
+ "email": "dev@gajaguar.com"
7
+ },
8
+ "plugins": [
9
+ {
10
+ "name": "clockify-cli",
11
+ "source": "./",
12
+ "description": "Skills that teach an agent to drive the unofficial Clockify CLI"
13
+ }
14
+ ]
15
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "clockify-cli",
3
+ "version": "1.0.0",
4
+ "description": "Clockify time-tracking and workspace skills for Claude Code",
5
+ "author": {
6
+ "name": "G.A.JAGUAR",
7
+ "email": "dev@gajaguar.com"
8
+ },
9
+ "license": "MIT",
10
+ "repository": "https://github.com/gajaguar/clockify-cli",
11
+ "keywords": [
12
+ "clockify",
13
+ "time-tracking",
14
+ "timesheet",
15
+ "cli",
16
+ "skill"
17
+ ]
18
+ }
@@ -0,0 +1,117 @@
1
+ # AGENTS.md
2
+
3
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
4
+ "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
5
+ interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt).
6
+
7
+ ## Agent instructions
8
+
9
+ - `AGENTS.md` is the only agent instructions file; put project rules here. The
10
+ repository MUST NOT contain a `CLAUDE.md` or any other tool-specific copy,
11
+ because a second copy drifts from this one.
12
+
13
+ ## Command surface
14
+
15
+ - Run the `Makefile` targets (`make check`, `make fix`, `make test`, ...)
16
+ instead of the underlying tools, so the agent and CI use the same options.
17
+ `make help` lists them.
18
+ - Give every new target a `##` help line; `make help` prints it.
19
+ - `make test` skips the `live` tests, which call the real Clockify API; run
20
+ them only with `CLOCKIFY_TEST_API_KEY` set.
21
+
22
+ ## Gate
23
+
24
+ - `make check` and `make test` MUST pass before any commit.
25
+ - Run `make fix` first for findings it can repair, then edit by hand.
26
+
27
+ ## Commits and branches
28
+
29
+ - Write commit messages as
30
+ [Conventional Commits](https://www.conventionalcommits.org/) and branch
31
+ names as [Conventional Branch](https://conventionalbranch.org/)
32
+ (`<type>/<description>`, e.g. `feat/add-login`). A pre-commit hook and
33
+ `make commits-check` enforce both; see
34
+ [`docs/conventions/commits-check.md`](docs/conventions/commits-check.md).
35
+ - Name a documentation or dependency branch `chore/...`: a branch type is not
36
+ a commit type, and `docs/` is not one.
37
+
38
+ ## Pull requests
39
+
40
+ Once a pull request is open, the agent MUST:
41
+
42
+ 1. Wait for CI; while it fails, fix the cause, push to the same branch and
43
+ wait again until it passes.
44
+ 2. Squash-merge a pull request with exactly one commit and use a regular merge
45
+ commit otherwise (`gh pr view --json commits` gives the count).
46
+ 3. Delete the branch on the remote and locally.
47
+ 4. Switch back to the base branch, pull it and run `git fetch --prune`.
48
+
49
+ ## Documentation
50
+
51
+ - Write documentation as an OKF bundle of atomic notes under `docs/`: one
52
+ Markdown concept per file, with YAML frontmatter (`type`, `title`,
53
+ `description`).
54
+ - Add a new note to its directory's `index.md` and, by file name, to
55
+ [`docs/log.md`](docs/log.md).
56
+ - Write a note only when it explains something a reader cannot already get
57
+ from `make help`, a linter's own message, or the configuration it comes
58
+ from.
59
+
60
+ ## Dependencies
61
+
62
+ - Add a new tool to the ecosystem manager that owns it; use `mise.toml` only
63
+ for a tool that bootstraps an ecosystem or has no manager in this
64
+ repository. See
65
+ [`docs/toolchain/layering-rule.md`](docs/toolchain/layering-rule.md).
66
+
67
+ ## Python
68
+
69
+ - Write no docstrings on functions, methods or classes; add a comment only
70
+ where the *why* is not obvious from the code. `pylint-gajaguar`'s
71
+ `gajaguar-no-docstrings` fails `make check` on any docstring.
72
+ - Enable the plugin with `enable = ["gajaguar"]` in `pyproject.toml`'s
73
+ `[tool.pylint."messages control"]`, not with a list of rules, so a rule
74
+ added by a `pylint-gajaguar` upgrade runs without a config change.
75
+ - Keep `pyproject.toml` to settings that differ from the tool's default, and
76
+ keep a `lint.per-file-ignores` entry only while it matches a current
77
+ violation; see
78
+ [`docs/python/pyproject-defaults.md`](docs/python/pyproject-defaults.md).
79
+
80
+ ## Architecture
81
+
82
+ - Read [Layering](docs/ARCHITECTURE.md#layering) before adding code:
83
+ dependencies point downward only, from `commands/` through `services/` and
84
+ `runtime/` to the SDK.
85
+ - Read [Authentication](docs/ARCHITECTURE.md#authentication) before changing
86
+ `auth/`: Clockify offers header API keys only, no OAuth2.
87
+ - Add a command by following
88
+ [Adding a command](docs/ARCHITECTURE.md#adding-a-command), which ends with
89
+ `make check`, `make test` and `make coverage-report`.
90
+
91
+ ## Clockify rules
92
+
93
+ - Reach Clockify only through `clockify-unofficial-sdk`, never with `httpx`
94
+ or a hand-built URL, so retries, pagination and models stay in one place.
95
+ Add a missing capability to the SDK first and raise the SDK floor in
96
+ `pyproject.toml` once it is released; see
97
+ [Cross-repo workflow](docs/ROADMAP.md#cross-repo-workflow).
98
+ - Expose each command module as `APP: Final = typer.Typer(...)`, decorate
99
+ every command with `@APP.command(help=...)` then `@handle_errors`, and
100
+ declare options inline as `Annotated[..., typer.Option(...)]`, because
101
+ Typer cannot resolve PEP 695 `type` aliases.
102
+ - Put logic beyond a single SDK call in `services/`.
103
+ - Render data only through `AppContext.render`, and write diagnostics to
104
+ stderr through `AppContext.notify` or `CliError`, so `-o json` output
105
+ stays parseable.
106
+ - Report a new failure with an existing `ExitCode`
107
+ (`runtime/exit_codes.py`). Its values are a public contract: add a value,
108
+ never renumber one.
109
+ - Read API keys from the credential stores only. A key MUST NOT be accepted
110
+ as a command-line argument or printed outside `auth token`, because shell
111
+ history and logs would keep it.
112
+ - Replace a long argument list with a Parameter Object (a frozen,
113
+ `slots=True` dataclass), as the SDK does, instead of silencing
114
+ `too-many-arguments`; see
115
+ [Typer parameter objects](docs/python/typer-parameter-objects.md).
116
+ - Update [`docs/coverage.md`](docs/coverage.md) with every endpoint change;
117
+ `make coverage-report` checks it against the OpenAPI document.
@@ -68,7 +68,7 @@ spell: ## Spell-check files with cspell — accepts FILES="..."
68
68
 
69
69
  commits-check: ## Validate the commit range and branch name against Conventional Commits/Branch — see docs/conventions/commits-check.md
70
70
  @git log --no-merges --format='%B%x00' $(BASE)..HEAD | while IFS= read -r -d '' message; do \
71
- message="$${message#$$'\n'}"; [ -z "$$message" ] && continue; \
71
+ message="$${message#$$'\n'}"; \
72
72
  echo "$$message" | $(CONVENTIONAL_GIT) check commit || exit 1; \
73
73
  done
74
74
  @$(CONVENTIONAL_GIT) check branch --name "$(BRANCH)"
@@ -1,18 +1,18 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: clockify-unofficial-cli
3
- Version: 0.2.0
3
+ Version: 1.0.0
4
4
  Summary: Unofficial command-line interface for Clockify, built on clockify-unofficial-sdk.
5
5
  Author-email: "G.A.JAGUAR" <dev@gajaguar.com>
6
6
  License-Expression: MIT
7
7
  License-File: LICENSE
8
8
  Keywords: cli,clockify,time-tracking
9
- Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Development Status :: 5 - Production/Stable
10
10
  Classifier: Environment :: Console
11
11
  Classifier: License :: OSI Approved :: MIT License
12
12
  Classifier: Programming Language :: Python :: 3.14
13
13
  Classifier: Typing :: Typed
14
14
  Requires-Python: >=3.14
15
- Requires-Dist: clockify-unofficial-sdk<2,>=1.0
15
+ Requires-Dist: clockify-unofficial-sdk<2,>=1.10
16
16
  Requires-Dist: keyring>=25.0
17
17
  Requires-Dist: platformdirs>=4.0
18
18
  Requires-Dist: pydantic>=2.9
@@ -42,6 +42,7 @@ Unofficial command-line interface for Clockify, built on
42
42
  - [Getting started](#getting-started)
43
43
  - [Prerequisites](#prerequisites)
44
44
  - [Installation](#installation)
45
+ - [Agent skills](#agent-skills)
45
46
  - [Usage](#usage)
46
47
  - [Commands](#commands)
47
48
  - [Global options](#global-options)
@@ -62,8 +63,9 @@ handles profiles, credential storage, name-to-ID resolution, output formatting,
62
63
  and stable exit codes. The SDK owns HTTP, retries, pagination, typed models,
63
64
  and API error mapping.
64
65
 
65
- The CLI is being delivered in phases. The authoritative endpoint mapping is in
66
- [`docs/coverage.md`](docs/coverage.md), and the delivery plan is in
66
+ `1.0.0` covers every Clockify operation that the SDK offers: 105 of the 166
67
+ non-deprecated operations. The authoritative endpoint mapping is in
68
+ [`docs/coverage.md`](docs/coverage.md), and what comes next is in
67
69
  [`docs/ROADMAP.md`](docs/ROADMAP.md).
68
70
 
69
71
  ## Key features
@@ -77,6 +79,8 @@ The CLI is being delivered in phases. The authoritative endpoint mapping is in
77
79
  - **Stable automation contract** - Keep data on stdout, diagnostics on stderr,
78
80
  and use documented exit codes.
79
81
  - **CI support** - Use `CLOCKIFY_API_KEY` without writing a credential to disk.
82
+ - **Broad coverage** - Manage time entries, projects, reports, time off,
83
+ approvals, expenses, invoices and webhooks from the terminal.
80
84
  - **Typed API foundation** - Build on the SDK's typed models, retries, and
81
85
  pagination instead of making HTTP requests in the CLI.
82
86
 
@@ -135,6 +139,34 @@ make install
135
139
  `make install` installs the pinned toolchain, Python dependencies, Node-based
136
140
  documentation tools, and the pre-commit hook.
137
141
 
142
+ ## Agent skills
143
+
144
+ The repository is also a Claude Code plugin. Pick one channel:
145
+
146
+ ```text
147
+ # Claude Code
148
+ /plugin marketplace add gajaguar/clockify-cli
149
+ /plugin install clockify-cli@clockify-cli-skills
150
+ ```
151
+
152
+ ```bash
153
+ # Any Agent Skills-compatible agent
154
+ npx skills add gajaguar/clockify-cli
155
+
156
+ # opencode
157
+ npx skills add gajaguar/clockify-cli -a opencode -y
158
+ ```
159
+
160
+ - `clockify-time-tracking` covers `start`, `stop`, `status`, `log` and `entry`.
161
+ - `clockify-cli` covers authentication, profiles, workspaces and the
162
+ project, task, tag, client, group, custom-field, webhook, report, time-off,
163
+ approval, expense and invoice commands.
164
+
165
+ The skills follow the [Agent Skills](https://agentskills.io/specification)
166
+ format, so other agents can read `skills/` directly. They require the CLI to
167
+ be installed and a user to have run `clockify auth login`. See
168
+ [`docs/agents/`](docs/agents/index.md) for what each install channel does.
169
+
138
170
  ## Usage
139
171
 
140
172
  Log in interactively. The key is read from a hidden prompt, validated with
@@ -165,9 +197,21 @@ printf '%s' "$KEY" | clockify -p work auth login --with-token --region EU_CENTRA
165
197
  - `config list` lists configured profiles.
166
198
  - `config use NAME` sets the default profile.
167
199
 
168
- Resource commands arrive in phases. See the
169
- [roadmap](docs/ROADMAP.md) and [coverage matrix](docs/coverage.md) for their
170
- status.
200
+ - `start`, `stop`, `status` and `log` track time.
201
+ - `workspace`, `user`, `client`, `project`, `task`, `tag`, `group`,
202
+ `custom-field`, `entry` and `webhook` manage resources.
203
+ - `report` and `shared-report` summarize tracked time.
204
+ - `time-off` and `approval` handle leave and timesheet approvals.
205
+ - `expense` and `invoice` handle expenses, receipts, invoices and payments.
206
+
207
+ Run `clockify COMMAND --help` for the verbs of each group. Clockify operations
208
+ that the SDK does not offer yet are listed as `planned` in the
209
+ [coverage matrix](docs/coverage.md) and in the [roadmap](docs/ROADMAP.md).
210
+
211
+ Commands that download a file (`expense receipt`, `invoice export`) write it to
212
+ `--save PATH`, or to a pipe with `--save -`, and never through `-o`. Amounts are
213
+ typed in major units (`120.50`) and shown in Clockify's minor units. Time off,
214
+ approvals, expenses and invoices need a paid Clockify plan.
171
215
 
172
216
  ### Global options
173
217
 
@@ -238,28 +282,27 @@ Live tests are excluded from `make test`. They require
238
282
 
239
283
  - Headless Linux hosts may not provide a Secret Service keyring backend. Use
240
284
  `CLOCKIFY_API_KEY` or the explicit `--insecure-storage` fallback.
241
- - The SDK is a `uv` git dependency pinned to a tag, so installation requires
242
- access to GitHub.
243
285
  - Python 3.14 is the minimum version required by the CLI and SDK.
244
286
 
245
287
  ## Roadmap
246
288
 
247
- - [x] Phase 0: foundation, authentication, profiles, renderers, and exit codes
248
- - [x] Phase 1: workspace, user, project, task, tag, group, and entry commands
249
- - [ ] Phase 2: core API completion
250
- - [ ] Phase 3: reports
251
- - [ ] Phase 4: time off, holidays, and approvals
252
- - [ ] Phase 5: expenses and invoices
253
- - [ ] Phase 6: scheduling, webhooks, and entity changes
254
- - [ ] Phase 7: release hardening for `1.0.0`
289
+ - [x] `0.2.0`: foundation, workspaces, users, projects, tasks, tags, entries
290
+ - [x] `0.3.0`: group membership and webhooks
291
+ - [x] `0.4.0`: reports
292
+ - [x] `0.5.0`: time off and approvals
293
+ - [x] `0.6.0`: expenses and receipts
294
+ - [x] `0.7.0`: invoices
295
+ - [x] `1.0.0`: exit codes, JSON output and command grammar are a stable contract
296
+ - [ ] `1.x`: the 61 operations the SDK does not offer yet, as it adds them
255
297
 
256
- See [`docs/ROADMAP.md`](docs/ROADMAP.md) for operation counts, dependencies,
257
- and completion criteria.
298
+ See [`docs/ROADMAP.md`](docs/ROADMAP.md) for operation counts and what is
299
+ frozen from `1.0.0`.
258
300
 
259
301
  ## Open items
260
302
 
261
303
  - Only the `GLOBAL` region host is verified against a live account.
262
- - Plan-gated endpoints need a paid workspace for live verification.
304
+ - Time off, approvals, expenses and invoices are tested against mocked
305
+ responses; they have not been run against a paid workspace.
263
306
  - Clockify's per-plan rate limits are not fully documented upstream.
264
307
 
265
308
  ## Contributing
@@ -19,6 +19,7 @@ Unofficial command-line interface for Clockify, built on
19
19
  - [Getting started](#getting-started)
20
20
  - [Prerequisites](#prerequisites)
21
21
  - [Installation](#installation)
22
+ - [Agent skills](#agent-skills)
22
23
  - [Usage](#usage)
23
24
  - [Commands](#commands)
24
25
  - [Global options](#global-options)
@@ -39,8 +40,9 @@ handles profiles, credential storage, name-to-ID resolution, output formatting,
39
40
  and stable exit codes. The SDK owns HTTP, retries, pagination, typed models,
40
41
  and API error mapping.
41
42
 
42
- The CLI is being delivered in phases. The authoritative endpoint mapping is in
43
- [`docs/coverage.md`](docs/coverage.md), and the delivery plan is in
43
+ `1.0.0` covers every Clockify operation that the SDK offers: 105 of the 166
44
+ non-deprecated operations. The authoritative endpoint mapping is in
45
+ [`docs/coverage.md`](docs/coverage.md), and what comes next is in
44
46
  [`docs/ROADMAP.md`](docs/ROADMAP.md).
45
47
 
46
48
  ## Key features
@@ -54,6 +56,8 @@ The CLI is being delivered in phases. The authoritative endpoint mapping is in
54
56
  - **Stable automation contract** - Keep data on stdout, diagnostics on stderr,
55
57
  and use documented exit codes.
56
58
  - **CI support** - Use `CLOCKIFY_API_KEY` without writing a credential to disk.
59
+ - **Broad coverage** - Manage time entries, projects, reports, time off,
60
+ approvals, expenses, invoices and webhooks from the terminal.
57
61
  - **Typed API foundation** - Build on the SDK's typed models, retries, and
58
62
  pagination instead of making HTTP requests in the CLI.
59
63
 
@@ -112,6 +116,34 @@ make install
112
116
  `make install` installs the pinned toolchain, Python dependencies, Node-based
113
117
  documentation tools, and the pre-commit hook.
114
118
 
119
+ ## Agent skills
120
+
121
+ The repository is also a Claude Code plugin. Pick one channel:
122
+
123
+ ```text
124
+ # Claude Code
125
+ /plugin marketplace add gajaguar/clockify-cli
126
+ /plugin install clockify-cli@clockify-cli-skills
127
+ ```
128
+
129
+ ```bash
130
+ # Any Agent Skills-compatible agent
131
+ npx skills add gajaguar/clockify-cli
132
+
133
+ # opencode
134
+ npx skills add gajaguar/clockify-cli -a opencode -y
135
+ ```
136
+
137
+ - `clockify-time-tracking` covers `start`, `stop`, `status`, `log` and `entry`.
138
+ - `clockify-cli` covers authentication, profiles, workspaces and the
139
+ project, task, tag, client, group, custom-field, webhook, report, time-off,
140
+ approval, expense and invoice commands.
141
+
142
+ The skills follow the [Agent Skills](https://agentskills.io/specification)
143
+ format, so other agents can read `skills/` directly. They require the CLI to
144
+ be installed and a user to have run `clockify auth login`. See
145
+ [`docs/agents/`](docs/agents/index.md) for what each install channel does.
146
+
115
147
  ## Usage
116
148
 
117
149
  Log in interactively. The key is read from a hidden prompt, validated with
@@ -142,9 +174,21 @@ printf '%s' "$KEY" | clockify -p work auth login --with-token --region EU_CENTRA
142
174
  - `config list` lists configured profiles.
143
175
  - `config use NAME` sets the default profile.
144
176
 
145
- Resource commands arrive in phases. See the
146
- [roadmap](docs/ROADMAP.md) and [coverage matrix](docs/coverage.md) for their
147
- status.
177
+ - `start`, `stop`, `status` and `log` track time.
178
+ - `workspace`, `user`, `client`, `project`, `task`, `tag`, `group`,
179
+ `custom-field`, `entry` and `webhook` manage resources.
180
+ - `report` and `shared-report` summarize tracked time.
181
+ - `time-off` and `approval` handle leave and timesheet approvals.
182
+ - `expense` and `invoice` handle expenses, receipts, invoices and payments.
183
+
184
+ Run `clockify COMMAND --help` for the verbs of each group. Clockify operations
185
+ that the SDK does not offer yet are listed as `planned` in the
186
+ [coverage matrix](docs/coverage.md) and in the [roadmap](docs/ROADMAP.md).
187
+
188
+ Commands that download a file (`expense receipt`, `invoice export`) write it to
189
+ `--save PATH`, or to a pipe with `--save -`, and never through `-o`. Amounts are
190
+ typed in major units (`120.50`) and shown in Clockify's minor units. Time off,
191
+ approvals, expenses and invoices need a paid Clockify plan.
148
192
 
149
193
  ### Global options
150
194
 
@@ -215,28 +259,27 @@ Live tests are excluded from `make test`. They require
215
259
 
216
260
  - Headless Linux hosts may not provide a Secret Service keyring backend. Use
217
261
  `CLOCKIFY_API_KEY` or the explicit `--insecure-storage` fallback.
218
- - The SDK is a `uv` git dependency pinned to a tag, so installation requires
219
- access to GitHub.
220
262
  - Python 3.14 is the minimum version required by the CLI and SDK.
221
263
 
222
264
  ## Roadmap
223
265
 
224
- - [x] Phase 0: foundation, authentication, profiles, renderers, and exit codes
225
- - [x] Phase 1: workspace, user, project, task, tag, group, and entry commands
226
- - [ ] Phase 2: core API completion
227
- - [ ] Phase 3: reports
228
- - [ ] Phase 4: time off, holidays, and approvals
229
- - [ ] Phase 5: expenses and invoices
230
- - [ ] Phase 6: scheduling, webhooks, and entity changes
231
- - [ ] Phase 7: release hardening for `1.0.0`
266
+ - [x] `0.2.0`: foundation, workspaces, users, projects, tasks, tags, entries
267
+ - [x] `0.3.0`: group membership and webhooks
268
+ - [x] `0.4.0`: reports
269
+ - [x] `0.5.0`: time off and approvals
270
+ - [x] `0.6.0`: expenses and receipts
271
+ - [x] `0.7.0`: invoices
272
+ - [x] `1.0.0`: exit codes, JSON output and command grammar are a stable contract
273
+ - [ ] `1.x`: the 61 operations the SDK does not offer yet, as it adds them
232
274
 
233
- See [`docs/ROADMAP.md`](docs/ROADMAP.md) for operation counts, dependencies,
234
- and completion criteria.
275
+ See [`docs/ROADMAP.md`](docs/ROADMAP.md) for operation counts and what is
276
+ frozen from `1.0.0`.
235
277
 
236
278
  ## Open items
237
279
 
238
280
  - Only the `GLOBAL` region host is verified against a live account.
239
- - Plan-gated endpoints need a paid workspace for live verification.
281
+ - Time off, approvals, expenses and invoices are tested against mocked
282
+ responses; they have not been run against a paid workspace.
240
283
  - Clockify's per-plan rate limits are not fully documented upstream.
241
284
 
242
285
  ## Contributing
@@ -1,5 +1,5 @@
1
1
  ---
2
- type: spec
2
+ type: reference
3
3
  title: Architecture and technical specification
4
4
  description: The stack, layering, design patterns, authentication and extension checklist of clockify-cli.
5
5
  tags: [architecture]
@@ -10,7 +10,7 @@ status: stable
10
10
 
11
11
  This document is the technical specification for `clockify-cli`: what it is,
12
12
  which stack and design patterns it uses and why, how authentication works,
13
- and how to extend it. The phased delivery plan lives in
13
+ and how to extend it. The release history and what comes next live in
14
14
  [`ROADMAP.md`](ROADMAP.md); the per-endpoint status lives in
15
15
  [`coverage.md`](coverage.md).
16
16
 
@@ -26,6 +26,7 @@ and how to extend it. The phased delivery plan lives in
26
26
  - [Configuration](#configuration)
27
27
  - [Errors and exit codes](#errors-and-exit-codes)
28
28
  - [Testing strategy](#testing-strategy)
29
+ - [Stability](#stability)
29
30
  - [Adding a command](#adding-a-command)
30
31
  - [Security considerations](#security-considerations)
31
32
  - [Open items](#open-items)
@@ -41,9 +42,9 @@ stable exit codes.
41
42
 
42
43
  Goals:
43
44
 
44
- - Expose **every non-deprecated Clockify API operation** (166 at the time of
45
- writing) as a typed CLI command — tracked row by row in
46
- [`coverage.md`](coverage.md).
45
+ - Expose **every non-deprecated Clockify API operation that the SDK offers**
46
+ as a typed CLI command: 105 of the 166 at `1.0.0`, tracked row by row in
47
+ [`coverage.md`](coverage.md). The rest wait for the SDK.
47
48
  - Be pleasant interactively (tables, prompts, names instead of IDs) **and**
48
49
  predictable in scripts (JSON/CSV/ID output, stderr for diagnostics, stable
49
50
  exit codes).
@@ -214,7 +215,7 @@ clockify-cli/
214
215
  ├── Makefile, mk/*.mk # check/fix/test surface; mk/clockify.mk
215
216
  ├── docs/
216
217
  │ ├── ARCHITECTURE.md # this document
217
- │ ├── ROADMAP.md # phases to 100% endpoint coverage
218
+ │ ├── ROADMAP.md # releases to 1.0.0 and what comes next
218
219
  │ └── coverage.md # endpoint → SDK method → CLI command
219
220
  ├── scripts/
220
221
  │ └── coverage_report.py # checks coverage.md against openapi.json
@@ -242,32 +243,32 @@ clockify-cli/
242
243
  │ │ ├── formats.py # table/json/jsonl/csv/id renderers
243
244
  │ │ ├── registry.py # OutputFormat → renderer factory
244
245
  │ │ └── columns.py # per-resource column specs
245
- │ ├── services/ # Phase 1+: resolve, timer, parsing
246
- │ └── commands/
247
- │ ├── auth.py # login/status/logout/token
248
- │ └── config.py # path/list/use
246
+ │ ├── services/ # resolve, parsing, listing, timer, reports, time_off,
247
+ │ │ # expenses, invoices, files, money
248
+ │ └── commands/ # one module per Clockify area (auth, project, entry,
249
+ │ # report, time_off, expense, invoice, webhook, ...)
249
250
  └── tests/
250
251
  ├── conftest.py # in-memory keyring, fake client, runner
251
252
  ├── unit/ # mirrors src/ layout, offline
252
253
  └── live/ # -m live, needs CLOCKIFY_TEST_API_KEY
253
254
  ```
254
255
 
255
- Planned additions follow the same shape:
256
+ New areas follow the same shape: one `commands/<domain>.py` per row group in
257
+ [`coverage.md`](coverage.md), with its logic in `services/`. Notable services:
256
258
 
257
- - `commands/<domain>.py` per row group in [`coverage.md`](coverage.md), for
258
- example `project.py`, `entry.py`, `report.py`, `time_off.py`;
259
- - `services/resolve.py` (name-or-ID lookup);
260
- - `services/timer.py` (start/stop/continue);
261
- - `services/parsing.py` (durations like `1h30m` and relative dates like
262
- `yesterday 09:00`).
259
+ - `services/resolve.py` resolves a name or ID for every resource;
260
+ - `services/parsing.py` reads durations like `1h30m` and instants like
261
+ `yesterday 09:00`;
262
+ - `services/files.py` writes binary downloads (receipts, exported invoices);
263
+ - `services/money.py` converts typed amounts to Clockify's minor units.
263
264
 
264
265
  ## Command surface
265
266
 
266
267
  - **Grammar.** `clockify [GLOBAL OPTIONS] <noun> <verb> [ARGS]`. The standard
267
268
  verbs are `list`, `get`, `create`, `update` and `delete`; domain verbs such
268
269
  as `archive`, `approve` or `export` are added where the API has them.
269
- Time tracking also gets root shortcuts: `start`, `stop`, `status`, `log`
270
- and `continue`.
270
+ Time tracking also gets root shortcuts: `start`, `stop`, `status` and
271
+ `log`.
271
272
  - **Global options** must come before the noun:
272
273
 
273
274
  | Option | Env var | Default |
@@ -278,7 +279,7 @@ Planned additions follow the same shape:
278
279
  | `--version` | | |
279
280
  | `--install-completion` | | Added by Typer |
280
281
 
281
- - **Names or IDs.** From Phase 1, arguments such as `PROJECT` or `TAG`
282
+ - **Names or IDs.** Arguments such as `PROJECT` or `TAG`
282
283
  accept either an ID or an exact, case-insensitive name. An ambiguous name
283
284
  is a usage error that lists the candidates.
284
285
  - **Output contract.**
@@ -286,7 +287,11 @@ Planned additions follow the same shape:
286
287
  to stderr.
287
288
  - `json` keeps Clockify's camelCase field names so it matches the API docs.
288
289
  - `id` prints one identifier per line for `xargs`.
289
- - **Destructive commands** (`delete`, bulk operations), from Phase 1: ask
290
+ - A command that downloads a file (`expense receipt`, `invoice export`)
291
+ writes the bytes to `--save PATH`, or to a pipe with `--save -`, and never
292
+ through `-o`.
293
+ - Report commands print a totals line to stderr, so stdout holds only rows.
294
+ - **Destructive commands** (`delete`, bulk operations): ask
290
295
  for confirmation on a TTY and require `--yes` otherwise.
291
296
 
292
297
  ## Configuration
@@ -348,16 +353,34 @@ by `hint: <next step>`.
348
353
  `CLOCKIFY_TEST_API_KEY` is set. The default run excludes them.
349
354
  - **Gates.**
350
355
  - `make check`: ruff `ALL`, mypy strict, Pyright, pylint with
351
- `pylint-plugin`, markdownlint and cspell.
356
+ `pylint-gajaguar`, markdownlint and cspell.
352
357
  - `make test`: 90% coverage floor.
353
358
  - `make coverage-report`: keeps `coverage.md` in sync with the upstream
354
359
  spec.
355
360
 
361
+ ## Stability
362
+
363
+ From `1.0.0` the following are a public contract, and a breaking change to
364
+ any of them needs a major version:
365
+
366
+ - the exit codes in [Errors and exit codes](#errors-and-exit-codes);
367
+ - the field names and types of `-o json` and `-o jsonl`, which keep Clockify's
368
+ camelCase names (adding a field is not breaking);
369
+ - what goes to stdout (rendered data) and what goes to stderr (prompts, totals,
370
+ progress and errors);
371
+ - the `clockify <noun> <verb>` grammar, the commands and options listed as
372
+ `done` in [`coverage.md`](coverage.md), the global options, the environment
373
+ variables and the keys of `config.toml`.
374
+
375
+ Table layout and column headers, the wording of messages and `--help`, and the
376
+ order of rows Clockify does not sort are not part of the contract. See
377
+ [`ROADMAP.md`](ROADMAP.md#what-100-freezes) for how new operations arrive.
378
+
356
379
  ## Adding a command
357
380
 
358
381
  1. Confirm the SDK exposes the endpoint (check the SDK's `docs/coverage.md`).
359
- If not, add it there first and release a tag. Then bump the tag in
360
- `[tool.uv.sources]`.
382
+ If not, add it there first and publish a release. Then raise the SDK
383
+ floor in `pyproject.toml`.
361
384
  2. Add or extend `commands/<domain>.py`:
362
385
  - Declare options inline with `Annotated[..., typer.Option(...)]`. Typer
363
386
  can't resolve PEP 695 `type` aliases.
@@ -394,5 +417,7 @@ by `hint: <next step>`.
394
417
  - Clockify's per-plan rate limits aren't documented upstream. The CLI relies
395
418
  on the SDK's retry policy and exits with code 8 once retries are
396
419
  exhausted.
397
- - Several endpoints in Phases 4–6 (time off, invoices, scheduling) need paid
398
- plans, so live verification depends on access to such a workspace.
420
+ - Time off, approvals, expenses and invoices need paid plans. Their commands
421
+ are tested against mocked responses built from the OpenAPI schemas, and have
422
+ not been run against a real paid workspace.
423
+ - Report totals are not part of `-o json`, because they go to stderr.