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.
- mcp_ticketer/__init__.py +3 -12
- mcp_ticketer/__version__.py +1 -1
- mcp_ticketer/adapters/aitrackdown.py +243 -11
- mcp_ticketer/adapters/github.py +15 -14
- mcp_ticketer/adapters/hybrid.py +11 -11
- mcp_ticketer/adapters/jira.py +22 -25
- mcp_ticketer/adapters/linear/adapter.py +9 -21
- mcp_ticketer/adapters/linear/client.py +2 -1
- mcp_ticketer/adapters/linear/mappers.py +2 -1
- mcp_ticketer/cache/memory.py +6 -5
- mcp_ticketer/cli/adapter_diagnostics.py +4 -2
- mcp_ticketer/cli/codex_configure.py +2 -2
- mcp_ticketer/cli/configure.py +7 -14
- mcp_ticketer/cli/diagnostics.py +2 -2
- mcp_ticketer/cli/discover.py +6 -11
- mcp_ticketer/cli/gemini_configure.py +2 -2
- mcp_ticketer/cli/linear_commands.py +6 -7
- mcp_ticketer/cli/main.py +218 -242
- mcp_ticketer/cli/mcp_configure.py +1 -2
- mcp_ticketer/cli/ticket_commands.py +27 -30
- mcp_ticketer/cli/utils.py +23 -22
- mcp_ticketer/core/__init__.py +3 -1
- mcp_ticketer/core/adapter.py +82 -13
- mcp_ticketer/core/config.py +27 -29
- mcp_ticketer/core/env_discovery.py +10 -10
- mcp_ticketer/core/env_loader.py +8 -8
- mcp_ticketer/core/http_client.py +16 -16
- mcp_ticketer/core/mappers.py +10 -10
- mcp_ticketer/core/models.py +50 -20
- mcp_ticketer/core/project_config.py +40 -34
- mcp_ticketer/core/registry.py +2 -2
- mcp_ticketer/mcp/dto.py +32 -32
- mcp_ticketer/mcp/response_builder.py +2 -2
- mcp_ticketer/mcp/server.py +17 -37
- mcp_ticketer/mcp/server_sdk.py +2 -2
- mcp_ticketer/mcp/tools/__init__.py +7 -9
- mcp_ticketer/mcp/tools/attachment_tools.py +3 -4
- mcp_ticketer/mcp/tools/comment_tools.py +2 -2
- mcp_ticketer/mcp/tools/hierarchy_tools.py +8 -8
- mcp_ticketer/mcp/tools/pr_tools.py +2 -2
- mcp_ticketer/mcp/tools/search_tools.py +6 -6
- mcp_ticketer/mcp/tools/ticket_tools.py +12 -12
- mcp_ticketer/queue/health_monitor.py +4 -4
- mcp_ticketer/queue/manager.py +2 -2
- mcp_ticketer/queue/queue.py +16 -16
- mcp_ticketer/queue/ticket_registry.py +7 -7
- mcp_ticketer/queue/worker.py +2 -2
- {mcp_ticketer-0.4.2.dist-info → mcp_ticketer-0.4.3.dist-info}/METADATA +61 -2
- mcp_ticketer-0.4.3.dist-info/RECORD +73 -0
- mcp_ticketer-0.4.2.dist-info/RECORD +0 -73
- {mcp_ticketer-0.4.2.dist-info → mcp_ticketer-0.4.3.dist-info}/WHEEL +0 -0
- {mcp_ticketer-0.4.2.dist-info → mcp_ticketer-0.4.3.dist-info}/entry_points.txt +0 -0
- {mcp_ticketer-0.4.2.dist-info → mcp_ticketer-0.4.3.dist-info}/licenses/LICENSE +0 -0
- {mcp_ticketer-0.4.2.dist-info → mcp_ticketer-0.4.3.dist-info}/top_level.txt +0 -0
mcp_ticketer/adapters/jira.py
CHANGED
|
@@ -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,
|
|
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,
|
|
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) ->
|
|
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:
|
|
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:
|
|
225
|
-
params:
|
|
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:
|
|
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]) ->
|
|
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:
|
|
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:
|
|
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) ->
|
|
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
|
-
) ->
|
|
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:
|
|
656
|
-
) -> list[
|
|
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[
|
|
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
|
-
) ->
|
|
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[
|
|
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:
|
|
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) ->
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
)
|
|
30
|
-
from .
|
|
31
|
-
|
|
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,
|
|
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,
|
|
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:
|
mcp_ticketer/cache/memory.py
CHANGED
|
@@ -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
|
|
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) ->
|
|
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:
|
|
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:
|
|
138
|
+
ttl: float | None = None,
|
|
138
139
|
key_prefix: str = "",
|
|
139
|
-
cache_instance:
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
mcp_ticketer/cli/configure.py
CHANGED
|
@@ -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
|
-
|
|
14
|
-
|
|
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:
|
|
444
|
-
api_key:
|
|
445
|
-
project_id:
|
|
446
|
-
team_id:
|
|
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:
|
mcp_ticketer/cli/diagnostics.py
CHANGED
|
@@ -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
|
|
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:
|
|
798
|
+
output_file: str | None = None,
|
|
799
799
|
json_output: bool = False,
|
|
800
800
|
) -> None:
|
|
801
801
|
"""Run comprehensive system diagnostics."""
|
mcp_ticketer/cli/discover.py
CHANGED
|
@@ -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
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
252
|
+
team_key: str | None = typer.Option(
|
|
254
253
|
None, "--team-key", "-k", help="Team key (e.g., '1M')"
|
|
255
254
|
),
|
|
256
|
-
team_id:
|
|
257
|
-
workspace:
|
|
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:
|
|
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:
|
|
387
|
+
team_id: str | None = typer.Option(
|
|
389
388
|
None, "--team-id", "-i", help="Team UUID to show info for"
|
|
390
389
|
),
|
|
391
390
|
):
|