mcp-ticketer 0.1.22__py3-none-any.whl → 0.1.24__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 +10 -10
- mcp_ticketer/__version__.py +1 -1
- mcp_ticketer/adapters/aitrackdown.py +15 -14
- mcp_ticketer/adapters/github.py +21 -20
- mcp_ticketer/adapters/hybrid.py +13 -12
- mcp_ticketer/adapters/jira.py +32 -27
- mcp_ticketer/adapters/linear.py +29 -26
- mcp_ticketer/cache/memory.py +2 -2
- mcp_ticketer/cli/auggie_configure.py +237 -0
- mcp_ticketer/cli/codex_configure.py +257 -0
- mcp_ticketer/cli/gemini_configure.py +261 -0
- mcp_ticketer/cli/main.py +171 -10
- mcp_ticketer/cli/migrate_config.py +3 -7
- mcp_ticketer/cli/utils.py +8 -8
- mcp_ticketer/core/adapter.py +12 -11
- mcp_ticketer/core/config.py +17 -17
- mcp_ticketer/core/env_discovery.py +24 -24
- mcp_ticketer/core/http_client.py +13 -13
- mcp_ticketer/core/mappers.py +25 -25
- mcp_ticketer/core/models.py +10 -10
- mcp_ticketer/core/project_config.py +25 -22
- mcp_ticketer/core/registry.py +7 -7
- mcp_ticketer/mcp/server.py +18 -18
- mcp_ticketer/queue/manager.py +2 -2
- mcp_ticketer/queue/queue.py +7 -7
- mcp_ticketer/queue/worker.py +8 -8
- {mcp_ticketer-0.1.22.dist-info → mcp_ticketer-0.1.24.dist-info}/METADATA +58 -8
- mcp_ticketer-0.1.24.dist-info/RECORD +45 -0
- mcp_ticketer-0.1.22.dist-info/RECORD +0 -42
- {mcp_ticketer-0.1.22.dist-info → mcp_ticketer-0.1.24.dist-info}/WHEEL +0 -0
- {mcp_ticketer-0.1.22.dist-info → mcp_ticketer-0.1.24.dist-info}/entry_points.txt +0 -0
- {mcp_ticketer-0.1.22.dist-info → mcp_ticketer-0.1.24.dist-info}/licenses/LICENSE +0 -0
- {mcp_ticketer-0.1.22.dist-info → mcp_ticketer-0.1.24.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"""Codex CLI configuration for mcp-ticketer integration.
|
|
2
|
+
|
|
3
|
+
Codex CLI only supports global configuration at ~/.codex/config.toml.
|
|
4
|
+
Unlike Claude Code and Gemini CLI, there is no project-level configuration support.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Dict, Optional
|
|
10
|
+
|
|
11
|
+
if sys.version_info >= (3, 11):
|
|
12
|
+
import tomllib
|
|
13
|
+
else:
|
|
14
|
+
import tomli as tomllib
|
|
15
|
+
|
|
16
|
+
import tomli_w
|
|
17
|
+
from rich.console import Console
|
|
18
|
+
|
|
19
|
+
from .mcp_configure import find_mcp_ticketer_binary, load_project_config
|
|
20
|
+
|
|
21
|
+
console = Console()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def find_codex_config() -> Path:
|
|
25
|
+
"""Find Codex CLI configuration file location.
|
|
26
|
+
|
|
27
|
+
Codex CLI ONLY supports global configuration at ~/.codex/config.toml.
|
|
28
|
+
No project-level or user-scoped configuration is available.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Path to Codex global config file at ~/.codex/config.toml
|
|
32
|
+
|
|
33
|
+
"""
|
|
34
|
+
# Codex only supports global config (no project-level support)
|
|
35
|
+
config_path = Path.home() / ".codex" / "config.toml"
|
|
36
|
+
return config_path
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def load_codex_config(config_path: Path) -> Dict[str, Any]:
|
|
40
|
+
"""Load existing Codex configuration or return empty structure.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
config_path: Path to Codex config.toml file
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Codex configuration dict with mcp_servers section
|
|
47
|
+
|
|
48
|
+
"""
|
|
49
|
+
if config_path.exists():
|
|
50
|
+
try:
|
|
51
|
+
with open(config_path, "rb") as f:
|
|
52
|
+
return tomllib.load(f)
|
|
53
|
+
except Exception as e:
|
|
54
|
+
console.print(
|
|
55
|
+
f"[yellow]⚠ Warning: Could not parse existing config: {e}[/yellow]"
|
|
56
|
+
)
|
|
57
|
+
console.print("[yellow]Creating new configuration...[/yellow]")
|
|
58
|
+
|
|
59
|
+
# Return empty structure with mcp_servers section
|
|
60
|
+
# NOTE: Use underscore mcp_servers, not camelCase mcpServers
|
|
61
|
+
return {"mcp_servers": {}}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def save_codex_config(config_path: Path, config: Dict[str, Any]) -> None:
|
|
65
|
+
"""Save Codex configuration to TOML file.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
config_path: Path to Codex config.toml file
|
|
69
|
+
config: Configuration to save
|
|
70
|
+
|
|
71
|
+
"""
|
|
72
|
+
# Ensure directory exists
|
|
73
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
74
|
+
|
|
75
|
+
# Write TOML with proper formatting
|
|
76
|
+
with open(config_path, "wb") as f:
|
|
77
|
+
tomli_w.dump(config, f)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def create_codex_server_config(
|
|
81
|
+
binary_path: str, project_config: dict, cwd: Optional[str] = None
|
|
82
|
+
) -> Dict[str, Any]:
|
|
83
|
+
"""Create Codex MCP server configuration for mcp-ticketer.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
binary_path: Path to mcp-ticketer binary
|
|
87
|
+
project_config: Project configuration from .mcp-ticketer/config.json
|
|
88
|
+
cwd: Working directory for server (optional, not used for global config)
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Codex MCP server configuration dict
|
|
92
|
+
|
|
93
|
+
"""
|
|
94
|
+
# Get adapter configuration
|
|
95
|
+
adapter = project_config.get("default_adapter", "aitrackdown")
|
|
96
|
+
adapters_config = project_config.get("adapters", {})
|
|
97
|
+
adapter_config = adapters_config.get(adapter, {})
|
|
98
|
+
|
|
99
|
+
# Build environment variables
|
|
100
|
+
env_vars: Dict[str, str] = {}
|
|
101
|
+
|
|
102
|
+
# Add PYTHONPATH if running from development environment
|
|
103
|
+
if cwd:
|
|
104
|
+
env_vars["PYTHONPATH"] = str(Path(cwd) / "src")
|
|
105
|
+
|
|
106
|
+
# Add adapter type
|
|
107
|
+
env_vars["MCP_TICKETER_ADAPTER"] = adapter
|
|
108
|
+
|
|
109
|
+
# Add adapter-specific environment variables
|
|
110
|
+
if adapter == "aitrackdown":
|
|
111
|
+
# Set base path for local adapter
|
|
112
|
+
base_path = adapter_config.get("base_path", ".aitrackdown")
|
|
113
|
+
if cwd:
|
|
114
|
+
# Use absolute path if cwd is provided
|
|
115
|
+
env_vars["MCP_TICKETER_BASE_PATH"] = str(Path(cwd) / base_path)
|
|
116
|
+
else:
|
|
117
|
+
env_vars["MCP_TICKETER_BASE_PATH"] = base_path
|
|
118
|
+
|
|
119
|
+
elif adapter == "linear":
|
|
120
|
+
if "api_key" in adapter_config:
|
|
121
|
+
env_vars["LINEAR_API_KEY"] = adapter_config["api_key"]
|
|
122
|
+
if "team_id" in adapter_config:
|
|
123
|
+
env_vars["LINEAR_TEAM_ID"] = adapter_config["team_id"]
|
|
124
|
+
|
|
125
|
+
elif adapter == "github":
|
|
126
|
+
if "token" in adapter_config:
|
|
127
|
+
env_vars["GITHUB_TOKEN"] = adapter_config["token"]
|
|
128
|
+
if "owner" in adapter_config:
|
|
129
|
+
env_vars["GITHUB_OWNER"] = adapter_config["owner"]
|
|
130
|
+
if "repo" in adapter_config:
|
|
131
|
+
env_vars["GITHUB_REPO"] = adapter_config["repo"]
|
|
132
|
+
|
|
133
|
+
elif adapter == "jira":
|
|
134
|
+
if "api_token" in adapter_config:
|
|
135
|
+
env_vars["JIRA_API_TOKEN"] = adapter_config["api_token"]
|
|
136
|
+
if "email" in adapter_config:
|
|
137
|
+
env_vars["JIRA_EMAIL"] = adapter_config["email"]
|
|
138
|
+
if "server" in adapter_config:
|
|
139
|
+
env_vars["JIRA_SERVER"] = adapter_config["server"]
|
|
140
|
+
if "project_key" in adapter_config:
|
|
141
|
+
env_vars["JIRA_PROJECT_KEY"] = adapter_config["project_key"]
|
|
142
|
+
|
|
143
|
+
# Create server configuration with Codex-specific structure
|
|
144
|
+
# NOTE: Codex uses nested dict structure for env vars
|
|
145
|
+
config: Dict[str, Any] = {
|
|
146
|
+
"command": binary_path,
|
|
147
|
+
"args": ["serve"],
|
|
148
|
+
"env": env_vars,
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return config
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def configure_codex_mcp(force: bool = False) -> None:
|
|
155
|
+
"""Configure Codex CLI to use mcp-ticketer.
|
|
156
|
+
|
|
157
|
+
IMPORTANT: Codex CLI ONLY supports global configuration at ~/.codex/config.toml.
|
|
158
|
+
There is no project-level or user-scoped configuration available.
|
|
159
|
+
|
|
160
|
+
After configuration, you must restart Codex CLI for changes to take effect.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
force: Overwrite existing configuration
|
|
164
|
+
|
|
165
|
+
Raises:
|
|
166
|
+
FileNotFoundError: If binary or project config not found
|
|
167
|
+
ValueError: If configuration is invalid
|
|
168
|
+
|
|
169
|
+
"""
|
|
170
|
+
# Step 1: Find mcp-ticketer binary
|
|
171
|
+
console.print("[cyan]🔍 Finding mcp-ticketer binary...[/cyan]")
|
|
172
|
+
try:
|
|
173
|
+
binary_path = find_mcp_ticketer_binary()
|
|
174
|
+
console.print(f"[green]✓[/green] Found: {binary_path}")
|
|
175
|
+
except FileNotFoundError as e:
|
|
176
|
+
console.print(f"[red]✗[/red] {e}")
|
|
177
|
+
raise
|
|
178
|
+
|
|
179
|
+
# Step 2: Load project configuration
|
|
180
|
+
console.print("\n[cyan]📖 Reading project configuration...[/cyan]")
|
|
181
|
+
try:
|
|
182
|
+
project_config = load_project_config()
|
|
183
|
+
adapter = project_config.get("default_adapter", "aitrackdown")
|
|
184
|
+
console.print(f"[green]✓[/green] Adapter: {adapter}")
|
|
185
|
+
except (FileNotFoundError, ValueError) as e:
|
|
186
|
+
console.print(f"[red]✗[/red] {e}")
|
|
187
|
+
raise
|
|
188
|
+
|
|
189
|
+
# Step 3: Find Codex config location (always global)
|
|
190
|
+
console.print("\n[cyan]🔧 Configuring Codex CLI (global-only)...[/cyan]")
|
|
191
|
+
console.print(
|
|
192
|
+
"[yellow]⚠ Note: Codex CLI only supports global configuration[/yellow]"
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
codex_config_path = find_codex_config()
|
|
196
|
+
console.print(f"[dim]Config location: {codex_config_path}[/dim]")
|
|
197
|
+
|
|
198
|
+
# Step 4: Load existing Codex configuration
|
|
199
|
+
codex_config = load_codex_config(codex_config_path)
|
|
200
|
+
|
|
201
|
+
# Step 5: Check if mcp-ticketer already configured
|
|
202
|
+
# NOTE: Use underscore mcp_servers, not camelCase
|
|
203
|
+
mcp_servers = codex_config.get("mcp_servers", {})
|
|
204
|
+
if "mcp-ticketer" in mcp_servers:
|
|
205
|
+
if not force:
|
|
206
|
+
console.print("[yellow]⚠ mcp-ticketer is already configured[/yellow]")
|
|
207
|
+
console.print("[dim]Use --force to overwrite existing configuration[/dim]")
|
|
208
|
+
return
|
|
209
|
+
else:
|
|
210
|
+
console.print("[yellow]⚠ Overwriting existing configuration[/yellow]")
|
|
211
|
+
|
|
212
|
+
# Step 6: Create mcp-ticketer server config
|
|
213
|
+
# For global config, include current working directory for context
|
|
214
|
+
cwd = str(Path.cwd())
|
|
215
|
+
server_config = create_codex_server_config(
|
|
216
|
+
binary_path=binary_path, project_config=project_config, cwd=cwd
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# Step 7: Update Codex configuration
|
|
220
|
+
if "mcp_servers" not in codex_config:
|
|
221
|
+
codex_config["mcp_servers"] = {}
|
|
222
|
+
|
|
223
|
+
codex_config["mcp_servers"]["mcp-ticketer"] = server_config
|
|
224
|
+
|
|
225
|
+
# Step 8: Save configuration
|
|
226
|
+
try:
|
|
227
|
+
save_codex_config(codex_config_path, codex_config)
|
|
228
|
+
console.print("\n[green]✓ Successfully configured mcp-ticketer[/green]")
|
|
229
|
+
console.print(f"[dim]Configuration saved to: {codex_config_path}[/dim]")
|
|
230
|
+
|
|
231
|
+
# Print configuration details
|
|
232
|
+
console.print("\n[bold]Configuration Details:[/bold]")
|
|
233
|
+
console.print(" Server name: mcp-ticketer")
|
|
234
|
+
console.print(f" Adapter: {adapter}")
|
|
235
|
+
console.print(f" Binary: {binary_path}")
|
|
236
|
+
console.print(" Scope: global (Codex only supports global config)")
|
|
237
|
+
console.print(f" Working directory: {cwd}")
|
|
238
|
+
if "env" in server_config:
|
|
239
|
+
console.print(
|
|
240
|
+
f" Environment variables: {list(server_config['env'].keys())}"
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
# Next steps
|
|
244
|
+
console.print("\n[bold cyan]Next Steps:[/bold cyan]")
|
|
245
|
+
console.print("1. [bold]Restart Codex CLI[/bold] (required for changes)")
|
|
246
|
+
console.print("2. Run 'codex' command from any directory")
|
|
247
|
+
console.print("3. mcp-ticketer tools will be available via MCP")
|
|
248
|
+
console.print(
|
|
249
|
+
"\n[yellow]⚠ Warning: This is a global configuration that affects all Codex sessions[/yellow]"
|
|
250
|
+
)
|
|
251
|
+
console.print(
|
|
252
|
+
"[yellow] The configuration includes paths from your current project directory[/yellow]"
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
except Exception as e:
|
|
256
|
+
console.print(f"\n[red]✗ Failed to save configuration:[/red] {e}")
|
|
257
|
+
raise
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"""Gemini CLI configuration for mcp-ticketer integration."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Literal, Optional
|
|
6
|
+
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
|
|
9
|
+
from .mcp_configure import find_mcp_ticketer_binary, load_project_config
|
|
10
|
+
|
|
11
|
+
console = Console()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def find_gemini_config(scope: Literal["project", "user"] = "project") -> Path:
|
|
15
|
+
"""Find or create Gemini CLI configuration file.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
scope: Configuration scope - "project" for .gemini/settings.json
|
|
19
|
+
or "user" for ~/.gemini/settings.json
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Path to Gemini settings file
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
if scope == "user":
|
|
26
|
+
# User-level configuration
|
|
27
|
+
config_path = Path.home() / ".gemini" / "settings.json"
|
|
28
|
+
else:
|
|
29
|
+
# Project-level configuration
|
|
30
|
+
config_path = Path.cwd() / ".gemini" / "settings.json"
|
|
31
|
+
|
|
32
|
+
return config_path
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def load_gemini_config(config_path: Path) -> dict:
|
|
36
|
+
"""Load existing Gemini configuration or return empty structure.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
config_path: Path to Gemini settings file
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Gemini configuration dict
|
|
43
|
+
|
|
44
|
+
"""
|
|
45
|
+
if config_path.exists():
|
|
46
|
+
try:
|
|
47
|
+
with open(config_path) as f:
|
|
48
|
+
return json.load(f)
|
|
49
|
+
except json.JSONDecodeError as e:
|
|
50
|
+
console.print(
|
|
51
|
+
f"[yellow]⚠ Warning: Could not parse existing config: {e}[/yellow]"
|
|
52
|
+
)
|
|
53
|
+
console.print("[yellow]Creating new configuration...[/yellow]")
|
|
54
|
+
|
|
55
|
+
# Return empty structure with mcpServers section
|
|
56
|
+
return {"mcpServers": {}}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def save_gemini_config(config_path: Path, config: dict) -> None:
|
|
60
|
+
"""Save Gemini configuration to file.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
config_path: Path to Gemini settings file
|
|
64
|
+
config: Configuration to save
|
|
65
|
+
|
|
66
|
+
"""
|
|
67
|
+
# Ensure directory exists
|
|
68
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
69
|
+
|
|
70
|
+
# Write with 2-space indentation (Gemini CLI standard)
|
|
71
|
+
with open(config_path, "w") as f:
|
|
72
|
+
json.dump(config, f, indent=2)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def create_gemini_server_config(
|
|
76
|
+
binary_path: str, project_config: dict, cwd: Optional[str] = None
|
|
77
|
+
) -> dict:
|
|
78
|
+
"""Create Gemini MCP server configuration for mcp-ticketer.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
binary_path: Path to mcp-ticketer binary
|
|
82
|
+
project_config: Project configuration from .mcp-ticketer/config.json
|
|
83
|
+
cwd: Working directory for server (optional)
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Gemini MCP server configuration dict
|
|
87
|
+
|
|
88
|
+
"""
|
|
89
|
+
# Get adapter configuration
|
|
90
|
+
adapter = project_config.get("default_adapter", "aitrackdown")
|
|
91
|
+
adapters_config = project_config.get("adapters", {})
|
|
92
|
+
adapter_config = adapters_config.get(adapter, {})
|
|
93
|
+
|
|
94
|
+
# Build environment variables
|
|
95
|
+
env_vars = {}
|
|
96
|
+
|
|
97
|
+
# Add PYTHONPATH if running from development environment
|
|
98
|
+
if cwd:
|
|
99
|
+
env_vars["PYTHONPATH"] = str(Path(cwd) / "src")
|
|
100
|
+
|
|
101
|
+
# Add adapter type
|
|
102
|
+
env_vars["MCP_TICKETER_ADAPTER"] = adapter
|
|
103
|
+
|
|
104
|
+
# Add adapter-specific environment variables
|
|
105
|
+
if adapter == "aitrackdown":
|
|
106
|
+
# Set base path for local adapter
|
|
107
|
+
base_path = adapter_config.get("base_path", ".aitrackdown")
|
|
108
|
+
if cwd:
|
|
109
|
+
# Use absolute path if cwd is provided
|
|
110
|
+
env_vars["MCP_TICKETER_BASE_PATH"] = str(Path(cwd) / base_path)
|
|
111
|
+
else:
|
|
112
|
+
env_vars["MCP_TICKETER_BASE_PATH"] = base_path
|
|
113
|
+
|
|
114
|
+
elif adapter == "linear":
|
|
115
|
+
if "api_key" in adapter_config:
|
|
116
|
+
env_vars["LINEAR_API_KEY"] = adapter_config["api_key"]
|
|
117
|
+
if "team_id" in adapter_config:
|
|
118
|
+
env_vars["LINEAR_TEAM_ID"] = adapter_config["team_id"]
|
|
119
|
+
|
|
120
|
+
elif adapter == "github":
|
|
121
|
+
if "token" in adapter_config:
|
|
122
|
+
env_vars["GITHUB_TOKEN"] = adapter_config["token"]
|
|
123
|
+
if "owner" in adapter_config:
|
|
124
|
+
env_vars["GITHUB_OWNER"] = adapter_config["owner"]
|
|
125
|
+
if "repo" in adapter_config:
|
|
126
|
+
env_vars["GITHUB_REPO"] = adapter_config["repo"]
|
|
127
|
+
|
|
128
|
+
elif adapter == "jira":
|
|
129
|
+
if "api_token" in adapter_config:
|
|
130
|
+
env_vars["JIRA_API_TOKEN"] = adapter_config["api_token"]
|
|
131
|
+
if "email" in adapter_config:
|
|
132
|
+
env_vars["JIRA_EMAIL"] = adapter_config["email"]
|
|
133
|
+
if "server" in adapter_config:
|
|
134
|
+
env_vars["JIRA_SERVER"] = adapter_config["server"]
|
|
135
|
+
if "project_key" in adapter_config:
|
|
136
|
+
env_vars["JIRA_PROJECT_KEY"] = adapter_config["project_key"]
|
|
137
|
+
|
|
138
|
+
# Create server configuration with Gemini-specific options
|
|
139
|
+
config = {
|
|
140
|
+
"command": binary_path,
|
|
141
|
+
"args": ["serve"],
|
|
142
|
+
"env": env_vars,
|
|
143
|
+
"timeout": 15000, # 15 seconds timeout
|
|
144
|
+
"trust": False, # Don't trust by default (security)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return config
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def configure_gemini_mcp(
|
|
151
|
+
scope: Literal["project", "user"] = "project", force: bool = False
|
|
152
|
+
) -> None:
|
|
153
|
+
"""Configure Gemini CLI to use mcp-ticketer.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
scope: Configuration scope - "project" or "user"
|
|
157
|
+
force: Overwrite existing configuration
|
|
158
|
+
|
|
159
|
+
Raises:
|
|
160
|
+
FileNotFoundError: If binary or project config not found
|
|
161
|
+
ValueError: If configuration is invalid
|
|
162
|
+
|
|
163
|
+
"""
|
|
164
|
+
# Step 1: Find mcp-ticketer binary
|
|
165
|
+
console.print("[cyan]🔍 Finding mcp-ticketer binary...[/cyan]")
|
|
166
|
+
try:
|
|
167
|
+
binary_path = find_mcp_ticketer_binary()
|
|
168
|
+
console.print(f"[green]✓[/green] Found: {binary_path}")
|
|
169
|
+
except FileNotFoundError as e:
|
|
170
|
+
console.print(f"[red]✗[/red] {e}")
|
|
171
|
+
raise
|
|
172
|
+
|
|
173
|
+
# Step 2: Load project configuration
|
|
174
|
+
console.print("\n[cyan]📖 Reading project configuration...[/cyan]")
|
|
175
|
+
try:
|
|
176
|
+
project_config = load_project_config()
|
|
177
|
+
adapter = project_config.get("default_adapter", "aitrackdown")
|
|
178
|
+
console.print(f"[green]✓[/green] Adapter: {adapter}")
|
|
179
|
+
except (FileNotFoundError, ValueError) as e:
|
|
180
|
+
console.print(f"[red]✗[/red] {e}")
|
|
181
|
+
raise
|
|
182
|
+
|
|
183
|
+
# Step 3: Find Gemini config location
|
|
184
|
+
config_type = "user-level" if scope == "user" else "project-level"
|
|
185
|
+
console.print(f"\n[cyan]🔧 Configuring {config_type} Gemini CLI...[/cyan]")
|
|
186
|
+
|
|
187
|
+
gemini_config_path = find_gemini_config(scope)
|
|
188
|
+
console.print(f"[dim]Config location: {gemini_config_path}[/dim]")
|
|
189
|
+
|
|
190
|
+
# Step 4: Load existing Gemini configuration
|
|
191
|
+
gemini_config = load_gemini_config(gemini_config_path)
|
|
192
|
+
|
|
193
|
+
# Step 5: Check if mcp-ticketer already configured
|
|
194
|
+
if "mcp-ticketer" in gemini_config.get("mcpServers", {}):
|
|
195
|
+
if not force:
|
|
196
|
+
console.print("[yellow]⚠ mcp-ticketer is already configured[/yellow]")
|
|
197
|
+
console.print("[dim]Use --force to overwrite existing configuration[/dim]")
|
|
198
|
+
return
|
|
199
|
+
else:
|
|
200
|
+
console.print("[yellow]⚠ Overwriting existing configuration[/yellow]")
|
|
201
|
+
|
|
202
|
+
# Step 6: Create mcp-ticketer server config
|
|
203
|
+
cwd = str(Path.cwd()) if scope == "project" else None
|
|
204
|
+
server_config = create_gemini_server_config(
|
|
205
|
+
binary_path=binary_path, project_config=project_config, cwd=cwd
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# Step 7: Update Gemini configuration
|
|
209
|
+
if "mcpServers" not in gemini_config:
|
|
210
|
+
gemini_config["mcpServers"] = {}
|
|
211
|
+
|
|
212
|
+
gemini_config["mcpServers"]["mcp-ticketer"] = server_config
|
|
213
|
+
|
|
214
|
+
# Step 8: Save configuration
|
|
215
|
+
try:
|
|
216
|
+
save_gemini_config(gemini_config_path, gemini_config)
|
|
217
|
+
console.print("\n[green]✓ Successfully configured mcp-ticketer[/green]")
|
|
218
|
+
console.print(f"[dim]Configuration saved to: {gemini_config_path}[/dim]")
|
|
219
|
+
|
|
220
|
+
# Print configuration details
|
|
221
|
+
console.print("\n[bold]Configuration Details:[/bold]")
|
|
222
|
+
console.print(" Server name: mcp-ticketer")
|
|
223
|
+
console.print(f" Adapter: {adapter}")
|
|
224
|
+
console.print(f" Binary: {binary_path}")
|
|
225
|
+
console.print(f" Timeout: {server_config['timeout']}ms")
|
|
226
|
+
console.print(f" Trust: {server_config['trust']}")
|
|
227
|
+
if cwd:
|
|
228
|
+
console.print(f" Working directory: {cwd}")
|
|
229
|
+
if "env" in server_config:
|
|
230
|
+
console.print(
|
|
231
|
+
f" Environment variables: {list(server_config['env'].keys())}"
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
# Next steps
|
|
235
|
+
console.print("\n[bold cyan]Next Steps:[/bold cyan]")
|
|
236
|
+
if scope == "user":
|
|
237
|
+
console.print("1. Gemini CLI will use this configuration globally")
|
|
238
|
+
console.print("2. Run 'gemini' command in any directory")
|
|
239
|
+
else:
|
|
240
|
+
console.print("1. Run 'gemini' command in this project directory")
|
|
241
|
+
console.print("2. Gemini CLI will detect project-level configuration")
|
|
242
|
+
console.print("3. mcp-ticketer tools will be available via MCP")
|
|
243
|
+
|
|
244
|
+
# Add .gemini to .gitignore for project-level config
|
|
245
|
+
if scope == "project":
|
|
246
|
+
gitignore_path = Path.cwd() / ".gitignore"
|
|
247
|
+
if gitignore_path.exists():
|
|
248
|
+
gitignore_content = gitignore_path.read_text()
|
|
249
|
+
if ".gemini" not in gitignore_content:
|
|
250
|
+
with open(gitignore_path, "a") as f:
|
|
251
|
+
f.write("\n# Gemini CLI\n.gemini/\n")
|
|
252
|
+
console.print("\n[dim]✓ Added .gemini/ to .gitignore[/dim]")
|
|
253
|
+
else:
|
|
254
|
+
# Create .gitignore if it doesn't exist
|
|
255
|
+
with open(gitignore_path, "w") as f:
|
|
256
|
+
f.write("# Gemini CLI\n.gemini/\n")
|
|
257
|
+
console.print("\n[dim]✓ Created .gitignore with .gemini/[/dim]")
|
|
258
|
+
|
|
259
|
+
except Exception as e:
|
|
260
|
+
console.print(f"\n[red]✗ Failed to save configuration:[/red] {e}")
|
|
261
|
+
raise
|