crowdtime-cli 0.8.2__tar.gz → 0.9.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 (36) hide show
  1. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/PKG-INFO +1 -1
  2. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/pyproject.toml +1 -1
  3. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/__init__.py +1 -1
  4. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/commands/invoice_cmd.py +55 -6
  5. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/skills/crowdtime/references/commands.md +24 -3
  6. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/skills/crowdtime/references/workflows.md +4 -1
  7. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/.gitignore +0 -0
  8. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/LICENSE +0 -0
  9. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/README.md +0 -0
  10. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/auth.py +0 -0
  11. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/client.py +0 -0
  12. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/commands/__init__.py +0 -0
  13. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/commands/ai_cmd.py +0 -0
  14. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/commands/auth_cmd.py +0 -0
  15. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/commands/billing_cmd.py +0 -0
  16. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/commands/clients_cmd.py +0 -0
  17. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/commands/config_cmd.py +0 -0
  18. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/commands/expense_cmd.py +0 -0
  19. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/commands/favorites_cmd.py +0 -0
  20. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/commands/log_cmd.py +0 -0
  21. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/commands/org_cmd.py +0 -0
  22. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/commands/payroll_cmd.py +0 -0
  23. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/commands/projects_cmd.py +0 -0
  24. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/commands/report_cmd.py +0 -0
  25. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/commands/skill_cmd.py +0 -0
  26. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/commands/tasks_cmd.py +0 -0
  27. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/commands/timer_cmd.py +0 -0
  28. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/commands/timesheet_cmd.py +0 -0
  29. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/config.py +0 -0
  30. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/formatters.py +0 -0
  31. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/main.py +0 -0
  32. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/models.py +0 -0
  33. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/oauth.py +0 -0
  34. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/resolvers.py +0 -0
  35. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/skills/crowdtime/SKILL.md +0 -0
  36. {crowdtime_cli-0.8.2 → crowdtime_cli-0.9.0}/src/crowdtime_cli/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crowdtime-cli
3
- Version: 0.8.2
3
+ Version: 0.9.0
4
4
  Summary: AI-powered time tracking CLI — a modern, developer-friendly alternative to Harvest
5
5
  Project-URL: Homepage, https://crowdtime.lat
6
6
  Project-URL: Documentation, https://crowdtime.lat/docs
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "crowdtime-cli"
3
- version = "0.8.2"
3
+ version = "0.9.0"
4
4
  description = "AI-powered time tracking CLI — a modern, developer-friendly alternative to Harvest"
5
5
  readme = "README.md"
6
6
  license = {text = "Proprietary"}
@@ -1,3 +1,3 @@
1
1
  """CrowdTime CLI - AI-powered time tracking from the command line."""
2
2
 
3
- __version__ = "0.8.2"
3
+ __version__ = "0.9.0"
@@ -267,6 +267,21 @@ def list_invoices(
267
267
  invoice_type: Optional[str] = typer.Option(
268
268
  None, "--type", "-t", help="Filter by type: standard, recurring, retainer."
269
269
  ),
270
+ date_from: Optional[str] = typer.Option(
271
+ None, "--from", help="Filter by issue date from (YYYY-MM-DD)."
272
+ ),
273
+ date_to: Optional[str] = typer.Option(
274
+ None, "--to", help="Filter by issue date to (YYYY-MM-DD)."
275
+ ),
276
+ search: Optional[str] = typer.Option(
277
+ None, "--search", "-q", help="Search by invoice number, subject, or client name."
278
+ ),
279
+ min_amount: Optional[float] = typer.Option(
280
+ None, "--min-amount", help="Filter by minimum total amount."
281
+ ),
282
+ max_amount: Optional[float] = typer.Option(
283
+ None, "--max-amount", help="Filter by maximum total amount."
284
+ ),
270
285
  output_json: bool = typer.Option(False, "--json", help="Output as JSON."),
271
286
  ) -> None:
272
287
  """List invoices in the current organization.
@@ -276,6 +291,9 @@ def list_invoices(
276
291
  ct invoice list --status sent
277
292
  ct invoice list --client <client-id> --status paid
278
293
  ct invoice list --type recurring
294
+ ct invoice list --from 2026-01-01 --to 2026-03-31
295
+ ct invoice list --search "INV-001"
296
+ ct invoice list --min-amount 1000 --max-amount 5000
279
297
  """
280
298
  client = CrowdTimeClient(require_auth=True, require_org=True)
281
299
 
@@ -286,6 +304,16 @@ def list_invoices(
286
304
  params["client"] = client_id
287
305
  if invoice_type:
288
306
  params["invoice_type"] = invoice_type
307
+ if date_from:
308
+ params["issue_date_from"] = date_from
309
+ if date_to:
310
+ params["issue_date_to"] = date_to
311
+ if search:
312
+ params["search"] = search
313
+ if min_amount is not None:
314
+ params["total_min"] = str(min_amount)
315
+ if max_amount is not None:
316
+ params["total_max"] = str(max_amount)
289
317
 
290
318
  try:
291
319
  data = client.get("/invoices/", params=params or None)
@@ -995,17 +1023,37 @@ def retainer_default(ctx: typer.Context) -> None:
995
1023
 
996
1024
  @retainer_app.command("list")
997
1025
  def list_retainers(
1026
+ status: Optional[str] = typer.Option(
1027
+ None, "--status", "-s", help="Filter by status: pending, active, paused, completed, cancelled."
1028
+ ),
1029
+ client_id: Optional[str] = typer.Option(
1030
+ None, "--client", "-c", help="Filter by client ID."
1031
+ ),
1032
+ search: Optional[str] = typer.Option(
1033
+ None, "--search", "-q", help="Search by retainer name or client name."
1034
+ ),
998
1035
  output_json: bool = typer.Option(False, "--json", help="Output as JSON."),
999
1036
  ) -> None:
1000
1037
  """List retainer agreements.
1001
1038
 
1002
1039
  Examples:
1003
1040
  ct invoice retainer list
1041
+ ct invoice retainer list --status active
1042
+ ct invoice retainer list --client <client-id>
1043
+ ct invoice retainer list --search "Acme"
1004
1044
  """
1005
1045
  client = CrowdTimeClient(require_auth=True, require_org=True)
1006
1046
 
1047
+ params: dict = {}
1048
+ if status:
1049
+ params["status"] = status
1050
+ if client_id:
1051
+ params["client"] = client_id
1052
+ if search:
1053
+ params["search"] = search
1054
+
1007
1055
  try:
1008
- data = client.get("/invoices/retainers/")
1056
+ data = client.get("/invoices/retainers/", params=params or None)
1009
1057
  results = data.get("results", []) if isinstance(data, dict) else data
1010
1058
 
1011
1059
  if output_json:
@@ -1020,21 +1068,22 @@ def list_retainers(
1020
1068
  table.add_column("Name", width=20)
1021
1069
  table.add_column("Client", width=18)
1022
1070
  table.add_column("Monthly", justify="right", width=12)
1023
- table.add_column("Included Hours", justify="right", width=15)
1071
+ table.add_column("Balance", justify="right", width=12)
1024
1072
  table.add_column("Status", width=10)
1025
1073
 
1026
1074
  for ret in results:
1027
- status = ret.get("status", "active")
1028
- style = "green" if status == "active" else "dim"
1075
+ ret_status = ret.get("status", "active")
1076
+ style = "green" if ret_status == "active" else "dim"
1029
1077
  currency = ret.get("currency", "USD")
1078
+ balance = Decimal(str(ret.get("balance", 0)))
1030
1079
 
1031
1080
  table.add_row(
1032
1081
  str(ret.get("id", "")),
1033
1082
  ret.get("name", ret.get("title", "")),
1034
1083
  ret.get("client_name", ""),
1035
1084
  _format_currency(ret.get("monthly_amount", ret.get("monthly", 0)), currency),
1036
- format_duration(Decimal(str(ret.get("included_hours", 0)))),
1037
- f"[{style}]{status.capitalize()}[/{style}]",
1085
+ f"[green]{_format_currency(balance, currency)}[/green]",
1086
+ f"[{style}]{ret_status.capitalize()}[/{style}]",
1038
1087
  )
1039
1088
 
1040
1089
  console.print(table)
@@ -1183,13 +1183,19 @@ Endpoint: `GET /timesheets/team-overview/`
1183
1183
  ### ct invoice list
1184
1184
 
1185
1185
  ```
1186
- ct invoice list [--status STATUS] [--client CLIENT] [--json]
1186
+ ct invoice list [--status STATUS] [--client CLIENT] [--type TYPE] [--from DATE] [--to DATE] [--search QUERY] [--min-amount NUM] [--max-amount NUM] [--json]
1187
1187
  ```
1188
1188
 
1189
1189
  | Option | Type | Description |
1190
1190
  |--------|------|-------------|
1191
- | `--status` | string | Filter: `draft`, `sent`, `viewed`, `partial`, `paid`, `overdue`, `void` |
1192
- | `--client` | string | Filter by client name or ID |
1191
+ | `--status` `-s` | string | Filter: `draft`, `sent`, `viewed`, `partial`, `paid`, `overdue`, `void` |
1192
+ | `--client` `-c` | string | Filter by client ID |
1193
+ | `--type` `-t` | string | Filter by type: `standard`, `recurring`, `retainer` |
1194
+ | `--from` | string | Filter by issue date from (YYYY-MM-DD) |
1195
+ | `--to` | string | Filter by issue date to (YYYY-MM-DD) |
1196
+ | `--search` `-q` | string | Search by invoice number, subject, or client name |
1197
+ | `--min-amount` | float | Filter by minimum total amount |
1198
+ | `--max-amount` | float | Filter by maximum total amount |
1193
1199
  | `--json` | flag | JSON output |
1194
1200
 
1195
1201
  Endpoint: `GET /invoices/`
@@ -1296,6 +1302,21 @@ Creates a copy of an existing invoice as a new draft. Endpoint: `POST /invoices/
1296
1302
 
1297
1303
  ---
1298
1304
 
1305
+ ### ct invoice retainer list
1306
+
1307
+ ```
1308
+ ct invoice retainer list [--status STATUS] [--client CLIENT] [--search QUERY] [--json]
1309
+ ```
1310
+
1311
+ | Option | Type | Description |
1312
+ |--------|------|-------------|
1313
+ | `--status` `-s` | string | Filter: `pending`, `active`, `paused`, `completed`, `cancelled` |
1314
+ | `--client` `-c` | string | Filter by client ID |
1315
+ | `--search` `-q` | string | Search by retainer name or client name |
1316
+ | `--json` | flag | JSON output |
1317
+
1318
+ Endpoint: `GET /invoices/retainers/`
1319
+
1299
1320
  ### ct invoice retainer show
1300
1321
 
1301
1322
  ```
@@ -574,8 +574,11 @@ ct invoice show <new-invoice-id>
574
574
  ### Set Up a Retainer and Track Balance
575
575
 
576
576
  ```bash
577
- # 1. List retainers
577
+ # 1. List retainers (with optional filters)
578
578
  ct invoice retainer list
579
+ ct invoice retainer list --status active
580
+ ct invoice retainer list --client <client-id>
581
+ ct invoice retainer list --search "Acme"
579
582
 
580
583
  # 2. View retainer details and balance
581
584
  ct invoice retainer show <retainer-id>
File without changes
File without changes
File without changes