mcp-ticketer 0.4.2__py3-none-any.whl → 0.4.3__py3-none-any.whl

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.

Potentially problematic release.


This version of mcp-ticketer might be problematic. Click here for more details.

Files changed (54) hide show
  1. mcp_ticketer/__init__.py +3 -12
  2. mcp_ticketer/__version__.py +1 -1
  3. mcp_ticketer/adapters/aitrackdown.py +243 -11
  4. mcp_ticketer/adapters/github.py +15 -14
  5. mcp_ticketer/adapters/hybrid.py +11 -11
  6. mcp_ticketer/adapters/jira.py +22 -25
  7. mcp_ticketer/adapters/linear/adapter.py +9 -21
  8. mcp_ticketer/adapters/linear/client.py +2 -1
  9. mcp_ticketer/adapters/linear/mappers.py +2 -1
  10. mcp_ticketer/cache/memory.py +6 -5
  11. mcp_ticketer/cli/adapter_diagnostics.py +4 -2
  12. mcp_ticketer/cli/codex_configure.py +2 -2
  13. mcp_ticketer/cli/configure.py +7 -14
  14. mcp_ticketer/cli/diagnostics.py +2 -2
  15. mcp_ticketer/cli/discover.py +6 -11
  16. mcp_ticketer/cli/gemini_configure.py +2 -2
  17. mcp_ticketer/cli/linear_commands.py +6 -7
  18. mcp_ticketer/cli/main.py +218 -242
  19. mcp_ticketer/cli/mcp_configure.py +1 -2
  20. mcp_ticketer/cli/ticket_commands.py +27 -30
  21. mcp_ticketer/cli/utils.py +23 -22
  22. mcp_ticketer/core/__init__.py +3 -1
  23. mcp_ticketer/core/adapter.py +82 -13
  24. mcp_ticketer/core/config.py +27 -29
  25. mcp_ticketer/core/env_discovery.py +10 -10
  26. mcp_ticketer/core/env_loader.py +8 -8
  27. mcp_ticketer/core/http_client.py +16 -16
  28. mcp_ticketer/core/mappers.py +10 -10
  29. mcp_ticketer/core/models.py +50 -20
  30. mcp_ticketer/core/project_config.py +40 -34
  31. mcp_ticketer/core/registry.py +2 -2
  32. mcp_ticketer/mcp/dto.py +32 -32
  33. mcp_ticketer/mcp/response_builder.py +2 -2
  34. mcp_ticketer/mcp/server.py +17 -37
  35. mcp_ticketer/mcp/server_sdk.py +2 -2
  36. mcp_ticketer/mcp/tools/__init__.py +7 -9
  37. mcp_ticketer/mcp/tools/attachment_tools.py +3 -4
  38. mcp_ticketer/mcp/tools/comment_tools.py +2 -2
  39. mcp_ticketer/mcp/tools/hierarchy_tools.py +8 -8
  40. mcp_ticketer/mcp/tools/pr_tools.py +2 -2
  41. mcp_ticketer/mcp/tools/search_tools.py +6 -6
  42. mcp_ticketer/mcp/tools/ticket_tools.py +12 -12
  43. mcp_ticketer/queue/health_monitor.py +4 -4
  44. mcp_ticketer/queue/manager.py +2 -2
  45. mcp_ticketer/queue/queue.py +16 -16
  46. mcp_ticketer/queue/ticket_registry.py +7 -7
  47. mcp_ticketer/queue/worker.py +2 -2
  48. {mcp_ticketer-0.4.2.dist-info → mcp_ticketer-0.4.3.dist-info}/METADATA +61 -2
  49. mcp_ticketer-0.4.3.dist-info/RECORD +73 -0
  50. mcp_ticketer-0.4.2.dist-info/RECORD +0 -73
  51. {mcp_ticketer-0.4.2.dist-info → mcp_ticketer-0.4.3.dist-info}/WHEEL +0 -0
  52. {mcp_ticketer-0.4.2.dist-info → mcp_ticketer-0.4.3.dist-info}/entry_points.txt +0 -0
  53. {mcp_ticketer-0.4.2.dist-info → mcp_ticketer-0.4.3.dist-info}/licenses/LICENSE +0 -0
  54. {mcp_ticketer-0.4.2.dist-info → mcp_ticketer-0.4.3.dist-info}/top_level.txt +0 -0
@@ -6,20 +6,21 @@ import logging
6
6
  import re
7
7
  from datetime import datetime
8
8
  from enum import Enum
9
- from typing import Any, Optional, Union
9
+ from typing import Any, Union
10
10
 
11
11
  import httpx
12
12
  from httpx import AsyncClient, HTTPStatusError, TimeoutException
13
13
 
14
14
  from ..core.adapter import BaseAdapter
15
15
  from ..core.env_loader import load_adapter_config, validate_adapter_config
16
- from ..core.models import Comment, Epic, Priority, SearchQuery, Task, TicketState
16
+ from ..core.models import (Comment, Epic, Priority, SearchQuery, Task,
17
+ TicketState)
17
18
  from ..core.registry import AdapterRegistry
18
19
 
19
20
  logger = logging.getLogger(__name__)
20
21
 
21
22
 
22
- def parse_jira_datetime(date_str: str) -> Optional[datetime]:
23
+ def parse_jira_datetime(date_str: str) -> datetime | None:
23
24
  """Parse JIRA datetime strings which can be in various formats.
24
25
 
25
26
  JIRA can return dates in formats like:
@@ -47,7 +48,7 @@ def parse_jira_datetime(date_str: str) -> Optional[datetime]:
47
48
  return None
48
49
 
49
50
 
50
- def extract_text_from_adf(adf_content: Union[str, dict[str, Any]]) -> str:
51
+ def extract_text_from_adf(adf_content: str | dict[str, Any]) -> str:
51
52
  """Extract plain text from Atlassian Document Format (ADF).
52
53
 
53
54
  Args:
@@ -221,8 +222,8 @@ class JiraAdapter(BaseAdapter[Union[Epic, Task]]):
221
222
  self,
222
223
  method: str,
223
224
  endpoint: str,
224
- data: Optional[dict[str, Any]] = None,
225
- params: Optional[dict[str, Any]] = None,
225
+ data: dict[str, Any] | None = None,
226
+ params: dict[str, Any] | None = None,
226
227
  retry_count: int = 0,
227
228
  ) -> dict[str, Any]:
228
229
  """Make HTTP request to JIRA API with retry logic.
@@ -287,7 +288,7 @@ class JiraAdapter(BaseAdapter[Union[Epic, Task]]):
287
288
  return self._priority_cache
288
289
 
289
290
  async def _get_issue_types(
290
- self, project_key: Optional[str] = None
291
+ self, project_key: str | None = None
291
292
  ) -> list[dict[str, Any]]:
292
293
  """Get available issue types for a project."""
293
294
  key = project_key or self.project_key
@@ -380,9 +381,7 @@ class JiraAdapter(BaseAdapter[Union[Epic, Task]]):
380
381
  }
381
382
  return mapping.get(priority, JiraPriority.MEDIUM)
382
383
 
383
- def _map_priority_from_jira(
384
- self, jira_priority: Optional[dict[str, Any]]
385
- ) -> Priority:
384
+ def _map_priority_from_jira(self, jira_priority: dict[str, Any] | None) -> Priority:
386
385
  """Map JIRA priority to universal priority."""
387
386
  if not jira_priority:
388
387
  return Priority.MEDIUM
@@ -432,7 +431,7 @@ class JiraAdapter(BaseAdapter[Union[Epic, Task]]):
432
431
  else:
433
432
  return TicketState.OPEN
434
433
 
435
- def _issue_to_ticket(self, issue: dict[str, Any]) -> Union[Epic, Task]:
434
+ def _issue_to_ticket(self, issue: dict[str, Any]) -> Epic | Task:
436
435
  """Convert JIRA issue to universal ticket model."""
437
436
  fields = issue.get("fields", {})
438
437
 
@@ -507,7 +506,7 @@ class JiraAdapter(BaseAdapter[Union[Epic, Task]]):
507
506
  )
508
507
 
509
508
  def _ticket_to_issue_fields(
510
- self, ticket: Union[Epic, Task], issue_type: Optional[str] = None
509
+ self, ticket: Epic | Task, issue_type: str | None = None
511
510
  ) -> dict[str, Any]:
512
511
  """Convert universal ticket to JIRA issue fields."""
513
512
  # Convert description to ADF format for JIRA Cloud
@@ -556,7 +555,7 @@ class JiraAdapter(BaseAdapter[Union[Epic, Task]]):
556
555
 
557
556
  return fields
558
557
 
559
- async def create(self, ticket: Union[Epic, Task]) -> Union[Epic, Task]:
558
+ async def create(self, ticket: Epic | Task) -> Epic | Task:
560
559
  """Create a new JIRA issue."""
561
560
  # Validate credentials before attempting operation
562
561
  is_valid, error_message = self.validate_credentials()
@@ -576,7 +575,7 @@ class JiraAdapter(BaseAdapter[Union[Epic, Task]]):
576
575
  created_issue = await self._make_request("GET", f"issue/{ticket.id}")
577
576
  return self._issue_to_ticket(created_issue)
578
577
 
579
- async def read(self, ticket_id: str) -> Optional[Union[Epic, Task]]:
578
+ async def read(self, ticket_id: str) -> Epic | Task | None:
580
579
  """Read a JIRA issue by key."""
581
580
  # Validate credentials before attempting operation
582
581
  is_valid, error_message = self.validate_credentials()
@@ -595,7 +594,7 @@ class JiraAdapter(BaseAdapter[Union[Epic, Task]]):
595
594
 
596
595
  async def update(
597
596
  self, ticket_id: str, updates: dict[str, Any]
598
- ) -> Optional[Union[Epic, Task]]:
597
+ ) -> Epic | Task | None:
599
598
  """Update a JIRA issue."""
600
599
  # Validate credentials before attempting operation
601
600
  is_valid, error_message = self.validate_credentials()
@@ -652,8 +651,8 @@ class JiraAdapter(BaseAdapter[Union[Epic, Task]]):
652
651
  raise
653
652
 
654
653
  async def list(
655
- self, limit: int = 10, offset: int = 0, filters: Optional[dict[str, Any]] = None
656
- ) -> list[Union[Epic, Task]]:
654
+ self, limit: int = 10, offset: int = 0, filters: dict[str, Any] | None = None
655
+ ) -> list[Epic | Task]:
657
656
  """List JIRA issues with pagination."""
658
657
  # Build JQL query
659
658
  jql_parts = []
@@ -692,7 +691,7 @@ class JiraAdapter(BaseAdapter[Union[Epic, Task]]):
692
691
  issues = data.get("issues", [])
693
692
  return [self._issue_to_ticket(issue) for issue in issues]
694
693
 
695
- async def search(self, query: SearchQuery) -> builtins.list[Union[Epic, Task]]:
694
+ async def search(self, query: SearchQuery) -> builtins.list[Epic | Task]:
696
695
  """Search JIRA issues using JQL."""
697
696
  # Build JQL query
698
697
  jql_parts = []
@@ -744,7 +743,7 @@ class JiraAdapter(BaseAdapter[Union[Epic, Task]]):
744
743
 
745
744
  async def transition_state(
746
745
  self, ticket_id: str, target_state: TicketState
747
- ) -> Optional[Union[Epic, Task]]:
746
+ ) -> Epic | Task | None:
748
747
  """Transition JIRA issue to a new state."""
749
748
  # Get available transitions
750
749
  transitions = await self._get_transitions(ticket_id)
@@ -858,9 +857,7 @@ class JiraAdapter(BaseAdapter[Union[Epic, Task]]):
858
857
 
859
858
  return comments
860
859
 
861
- async def get_project_info(
862
- self, project_key: Optional[str] = None
863
- ) -> dict[str, Any]:
860
+ async def get_project_info(self, project_key: str | None = None) -> dict[str, Any]:
864
861
  """Get JIRA project information including workflows and fields."""
865
862
  key = project_key or self.project_key
866
863
  if not key:
@@ -882,7 +879,7 @@ class JiraAdapter(BaseAdapter[Union[Epic, Task]]):
882
879
 
883
880
  async def execute_jql(
884
881
  self, jql: str, limit: int = 50
885
- ) -> builtins.list[Union[Epic, Task]]:
882
+ ) -> builtins.list[Epic | Task]:
886
883
  """Execute a raw JQL query.
887
884
 
888
885
  Args:
@@ -908,7 +905,7 @@ class JiraAdapter(BaseAdapter[Union[Epic, Task]]):
908
905
  return [self._issue_to_ticket(issue) for issue in issues]
909
906
 
910
907
  async def get_sprints(
911
- self, board_id: Optional[int] = None
908
+ self, board_id: int | None = None
912
909
  ) -> builtins.list[dict[str, Any]]:
913
910
  """Get active sprints for a board (requires JIRA Software).
914
911
 
@@ -992,7 +989,7 @@ class JiraAdapter(BaseAdapter[Union[Epic, Task]]):
992
989
  except Exception:
993
990
  return []
994
991
 
995
- async def get_current_user(self) -> Optional[dict[str, Any]]:
992
+ async def get_current_user(self) -> dict[str, Any] | None:
996
993
  """Get current authenticated user information."""
997
994
  try:
998
995
  return await self._make_request("GET", "myself")
@@ -20,27 +20,15 @@ from ...core.adapter import BaseAdapter
20
20
  from ...core.models import Comment, Epic, SearchQuery, Task, TicketState
21
21
  from ...core.registry import AdapterRegistry
22
22
  from .client import LinearGraphQLClient
23
- from .mappers import (
24
- build_linear_issue_input,
25
- build_linear_issue_update_input,
26
- map_linear_comment_to_comment,
27
- map_linear_issue_to_task,
28
- map_linear_project_to_epic,
29
- )
30
- from .queries import (
31
- ALL_FRAGMENTS,
32
- CREATE_ISSUE_MUTATION,
33
- LIST_ISSUES_QUERY,
34
- SEARCH_ISSUES_QUERY,
35
- UPDATE_ISSUE_MUTATION,
36
- WORKFLOW_STATES_QUERY,
37
- )
38
- from .types import (
39
- LinearStateMapping,
40
- build_issue_filter,
41
- get_linear_priority,
42
- get_linear_state_type,
43
- )
23
+ from .mappers import (build_linear_issue_input,
24
+ build_linear_issue_update_input,
25
+ map_linear_comment_to_comment, map_linear_issue_to_task,
26
+ map_linear_project_to_epic)
27
+ from .queries import (ALL_FRAGMENTS, CREATE_ISSUE_MUTATION, LIST_ISSUES_QUERY,
28
+ SEARCH_ISSUES_QUERY, UPDATE_ISSUE_MUTATION,
29
+ WORKFLOW_STATES_QUERY)
30
+ from .types import (LinearStateMapping, build_issue_filter,
31
+ get_linear_priority, get_linear_state_type)
44
32
 
45
33
 
46
34
  class LinearAdapter(BaseAdapter[Task]):
@@ -16,7 +16,8 @@ except ImportError:
16
16
  HTTPXAsyncTransport = None
17
17
  TransportError = Exception
18
18
 
19
- from ...core.exceptions import AdapterError, AuthenticationError, RateLimitError
19
+ from ...core.exceptions import (AdapterError, AuthenticationError,
20
+ RateLimitError)
20
21
 
21
22
 
22
23
  class LinearGraphQLClient:
@@ -6,7 +6,8 @@ from datetime import datetime
6
6
  from typing import Any
7
7
 
8
8
  from ...core.models import Comment, Epic, Priority, Task, TicketState
9
- from .types import extract_linear_metadata, get_universal_priority, get_universal_state
9
+ from .types import (extract_linear_metadata, get_universal_priority,
10
+ get_universal_state)
10
11
 
11
12
 
12
13
  def map_linear_issue_to_task(issue_data: dict[str, Any]) -> Task:
@@ -4,8 +4,9 @@ import asyncio
4
4
  import hashlib
5
5
  import json
6
6
  import time
7
+ from collections.abc import Callable
7
8
  from functools import wraps
8
- from typing import Any, Callable, Optional
9
+ from typing import Any
9
10
 
10
11
 
11
12
  class CacheEntry:
@@ -41,7 +42,7 @@ class MemoryCache:
41
42
  self._default_ttl = default_ttl
42
43
  self._lock = asyncio.Lock()
43
44
 
44
- async def get(self, key: str) -> Optional[Any]:
45
+ async def get(self, key: str) -> Any | None:
45
46
  """Get value from cache.
46
47
 
47
48
  Args:
@@ -60,7 +61,7 @@ class MemoryCache:
60
61
  del self._cache[key]
61
62
  return None
62
63
 
63
- async def set(self, key: str, value: Any, ttl: Optional[float] = None) -> None:
64
+ async def set(self, key: str, value: Any, ttl: float | None = None) -> None:
64
65
  """Set value in cache.
65
66
 
66
67
  Args:
@@ -134,9 +135,9 @@ class MemoryCache:
134
135
 
135
136
 
136
137
  def cache_decorator(
137
- ttl: Optional[float] = None,
138
+ ttl: float | None = None,
138
139
  key_prefix: str = "",
139
- cache_instance: Optional[MemoryCache] = None,
140
+ cache_instance: MemoryCache | None = None,
140
141
  ) -> Callable:
141
142
  """Decorator for caching async function results.
142
143
 
@@ -197,7 +197,8 @@ def _test_adapter_instantiation(console: Console) -> None:
197
197
  if primary:
198
198
  adapter_type = primary.adapter_type
199
199
  # Build config from discovery
200
- from ..mcp.server import _build_adapter_config_from_env_vars
200
+ from ..mcp.server import \
201
+ _build_adapter_config_from_env_vars
201
202
 
202
203
  config = _build_adapter_config_from_env_vars(adapter_type, {})
203
204
  else:
@@ -384,7 +385,8 @@ def get_adapter_status() -> dict[str, Any]:
384
385
  adapter_type = primary.adapter_type
385
386
  status["configuration_source"] = primary.found_in
386
387
  # Build basic config
387
- from ..mcp.server import _build_adapter_config_from_env_vars
388
+ from ..mcp.server import \
389
+ _build_adapter_config_from_env_vars
388
390
 
389
391
  config = _build_adapter_config_from_env_vars(adapter_type, {})
390
392
  else:
@@ -6,7 +6,7 @@ Unlike Claude Code and Gemini CLI, there is no project-level configuration suppo
6
6
 
7
7
  import sys
8
8
  from pathlib import Path
9
- from typing import Any, Optional
9
+ from typing import Any
10
10
 
11
11
  if sys.version_info >= (3, 11):
12
12
  import tomllib
@@ -78,7 +78,7 @@ def save_codex_config(config_path: Path, config: dict[str, Any]) -> None:
78
78
 
79
79
 
80
80
  def create_codex_server_config(
81
- binary_path: str, project_config: dict, cwd: Optional[str] = None
81
+ binary_path: str, project_config: dict, cwd: str | None = None
82
82
  ) -> dict[str, Any]:
83
83
  """Create Codex MCP server configuration for mcp-ticketer.
84
84
 
@@ -1,7 +1,6 @@
1
1
  """Interactive configuration wizard for MCP Ticketer."""
2
2
 
3
3
  import os
4
- from typing import Optional
5
4
 
6
5
  import typer
7
6
  from rich.console import Console
@@ -9,15 +8,9 @@ from rich.panel import Panel
9
8
  from rich.prompt import Confirm, Prompt
10
9
  from rich.table import Table
11
10
 
12
- from ..core.project_config import (
13
- AdapterConfig,
14
- AdapterType,
15
- ConfigResolver,
16
- ConfigValidator,
17
- HybridConfig,
18
- SyncStrategy,
19
- TicketerConfig,
20
- )
11
+ from ..core.project_config import (AdapterConfig, AdapterType, ConfigResolver,
12
+ ConfigValidator, HybridConfig, SyncStrategy,
13
+ TicketerConfig)
21
14
 
22
15
  console = Console()
23
16
 
@@ -440,10 +433,10 @@ def show_current_config() -> None:
440
433
 
441
434
 
442
435
  def set_adapter_config(
443
- adapter: Optional[str] = None,
444
- api_key: Optional[str] = None,
445
- project_id: Optional[str] = None,
446
- team_id: Optional[str] = None,
436
+ adapter: str | None = None,
437
+ api_key: str | None = None,
438
+ project_id: str | None = None,
439
+ team_id: str | None = None,
447
440
  global_scope: bool = False,
448
441
  **kwargs,
449
442
  ) -> None:
@@ -5,7 +5,7 @@ import logging
5
5
  import sys
6
6
  from datetime import datetime, timedelta
7
7
  from pathlib import Path
8
- from typing import Any, Optional
8
+ from typing import Any
9
9
 
10
10
  import typer
11
11
  from rich.console import Console
@@ -795,7 +795,7 @@ class SystemDiagnostics:
795
795
 
796
796
 
797
797
  async def run_diagnostics(
798
- output_file: Optional[str] = None,
798
+ output_file: str | None = None,
799
799
  json_output: bool = False,
800
800
  ) -> None:
801
801
  """Run comprehensive system diagnostics."""
@@ -1,18 +1,13 @@
1
1
  """CLI command for auto-discovering configuration from .env files."""
2
2
 
3
3
  from pathlib import Path
4
- from typing import Optional
5
4
 
6
5
  import typer
7
6
  from rich.console import Console
8
7
 
9
8
  from ..core.env_discovery import DiscoveredAdapter, EnvDiscovery
10
- from ..core.project_config import (
11
- AdapterConfig,
12
- ConfigResolver,
13
- ConfigValidator,
14
- TicketerConfig,
15
- )
9
+ from ..core.project_config import (AdapterConfig, ConfigResolver,
10
+ ConfigValidator, TicketerConfig)
16
11
 
17
12
  console = Console()
18
13
  app = typer.Typer(help="Auto-discover configuration from .env files")
@@ -93,7 +88,7 @@ def _display_discovered_adapter(
93
88
 
94
89
  @app.command()
95
90
  def show(
96
- project_path: Optional[Path] = typer.Option(
91
+ project_path: Path | None = typer.Option(
97
92
  None,
98
93
  "--path",
99
94
  "-p",
@@ -148,7 +143,7 @@ def show(
148
143
 
149
144
  @app.command()
150
145
  def save(
151
- adapter: Optional[str] = typer.Option(
146
+ adapter: str | None = typer.Option(
152
147
  None, "--adapter", "-a", help="Which adapter to save (defaults to recommended)"
153
148
  ),
154
149
  global_config: bool = typer.Option(
@@ -157,7 +152,7 @@ def save(
157
152
  dry_run: bool = typer.Option(
158
153
  False, "--dry-run", help="Show what would be saved without saving"
159
154
  ),
160
- project_path: Optional[Path] = typer.Option(
155
+ project_path: Path | None = typer.Option(
161
156
  None,
162
157
  "--path",
163
158
  "-p",
@@ -261,7 +256,7 @@ def save(
261
256
 
262
257
  @app.command()
263
258
  def interactive(
264
- project_path: Optional[Path] = typer.Option(
259
+ project_path: Path | None = typer.Option(
265
260
  None,
266
261
  "--path",
267
262
  "-p",
@@ -2,7 +2,7 @@
2
2
 
3
3
  import json
4
4
  from pathlib import Path
5
- from typing import Literal, Optional
5
+ from typing import Literal
6
6
 
7
7
  from rich.console import Console
8
8
 
@@ -73,7 +73,7 @@ def save_gemini_config(config_path: Path, config: dict) -> None:
73
73
 
74
74
 
75
75
  def create_gemini_server_config(
76
- binary_path: str, project_config: dict, cwd: Optional[str] = None
76
+ binary_path: str, project_config: dict, cwd: str | None = None
77
77
  ) -> dict:
78
78
  """Create Gemini MCP server configuration for mcp-ticketer.
79
79
 
@@ -1,7 +1,6 @@
1
1
  """Linear-specific CLI commands for workspace and team management."""
2
2
 
3
3
  import os
4
- from typing import Optional
5
4
 
6
5
  import typer
7
6
  from gql import Client, gql
@@ -80,7 +79,7 @@ def list_workspaces():
80
79
 
81
80
  @app.command("teams")
82
81
  def list_teams(
83
- workspace: Optional[str] = typer.Option(
82
+ workspace: str | None = typer.Option(
84
83
  None, "--workspace", "-w", help="Workspace URL key (optional)"
85
84
  ),
86
85
  all_teams: bool = typer.Option(
@@ -250,11 +249,11 @@ def list_teams(
250
249
 
251
250
  @app.command("configure")
252
251
  def configure_team(
253
- team_key: Optional[str] = typer.Option(
252
+ team_key: str | None = typer.Option(
254
253
  None, "--team-key", "-k", help="Team key (e.g., '1M')"
255
254
  ),
256
- team_id: Optional[str] = typer.Option(None, "--team-id", "-i", help="Team UUID"),
257
- workspace: Optional[str] = typer.Option(
255
+ team_id: str | None = typer.Option(None, "--team-id", "-i", help="Team UUID"),
256
+ workspace: str | None = typer.Option(
258
257
  None, "--workspace", "-w", help="Workspace URL key"
259
258
  ),
260
259
  ):
@@ -382,10 +381,10 @@ def configure_team(
382
381
 
383
382
  @app.command("info")
384
383
  def show_info(
385
- team_key: Optional[str] = typer.Option(
384
+ team_key: str | None = typer.Option(
386
385
  None, "--team-key", "-k", help="Team key to show info for"
387
386
  ),
388
- team_id: Optional[str] = typer.Option(
387
+ team_id: str | None = typer.Option(
389
388
  None, "--team-id", "-i", help="Team UUID to show info for"
390
389
  ),
391
390
  ):