mcp-ticketer 0.12.0__py3-none-any.whl → 2.2.13__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 (129) hide show
  1. mcp_ticketer/__init__.py +10 -10
  2. mcp_ticketer/__version__.py +1 -1
  3. mcp_ticketer/_version_scm.py +1 -0
  4. mcp_ticketer/adapters/aitrackdown.py +507 -6
  5. mcp_ticketer/adapters/asana/adapter.py +229 -0
  6. mcp_ticketer/adapters/asana/mappers.py +14 -0
  7. mcp_ticketer/adapters/github/__init__.py +26 -0
  8. mcp_ticketer/adapters/github/adapter.py +3229 -0
  9. mcp_ticketer/adapters/github/client.py +335 -0
  10. mcp_ticketer/adapters/github/mappers.py +797 -0
  11. mcp_ticketer/adapters/github/queries.py +692 -0
  12. mcp_ticketer/adapters/github/types.py +460 -0
  13. mcp_ticketer/adapters/hybrid.py +47 -5
  14. mcp_ticketer/adapters/jira/__init__.py +35 -0
  15. mcp_ticketer/adapters/jira/adapter.py +1351 -0
  16. mcp_ticketer/adapters/jira/client.py +271 -0
  17. mcp_ticketer/adapters/jira/mappers.py +246 -0
  18. mcp_ticketer/adapters/jira/queries.py +216 -0
  19. mcp_ticketer/adapters/jira/types.py +304 -0
  20. mcp_ticketer/adapters/linear/adapter.py +2730 -139
  21. mcp_ticketer/adapters/linear/client.py +175 -3
  22. mcp_ticketer/adapters/linear/mappers.py +203 -8
  23. mcp_ticketer/adapters/linear/queries.py +280 -3
  24. mcp_ticketer/adapters/linear/types.py +120 -4
  25. mcp_ticketer/analysis/__init__.py +56 -0
  26. mcp_ticketer/analysis/dependency_graph.py +255 -0
  27. mcp_ticketer/analysis/health_assessment.py +304 -0
  28. mcp_ticketer/analysis/orphaned.py +218 -0
  29. mcp_ticketer/analysis/project_status.py +594 -0
  30. mcp_ticketer/analysis/similarity.py +224 -0
  31. mcp_ticketer/analysis/staleness.py +266 -0
  32. mcp_ticketer/automation/__init__.py +11 -0
  33. mcp_ticketer/automation/project_updates.py +378 -0
  34. mcp_ticketer/cli/adapter_diagnostics.py +3 -1
  35. mcp_ticketer/cli/auggie_configure.py +17 -5
  36. mcp_ticketer/cli/codex_configure.py +97 -61
  37. mcp_ticketer/cli/configure.py +1288 -105
  38. mcp_ticketer/cli/cursor_configure.py +314 -0
  39. mcp_ticketer/cli/diagnostics.py +13 -12
  40. mcp_ticketer/cli/discover.py +5 -0
  41. mcp_ticketer/cli/gemini_configure.py +17 -5
  42. mcp_ticketer/cli/init_command.py +880 -0
  43. mcp_ticketer/cli/install_mcp_server.py +418 -0
  44. mcp_ticketer/cli/instruction_commands.py +6 -0
  45. mcp_ticketer/cli/main.py +267 -3175
  46. mcp_ticketer/cli/mcp_configure.py +821 -119
  47. mcp_ticketer/cli/mcp_server_commands.py +415 -0
  48. mcp_ticketer/cli/platform_detection.py +77 -12
  49. mcp_ticketer/cli/platform_installer.py +545 -0
  50. mcp_ticketer/cli/project_update_commands.py +350 -0
  51. mcp_ticketer/cli/setup_command.py +795 -0
  52. mcp_ticketer/cli/simple_health.py +12 -10
  53. mcp_ticketer/cli/ticket_commands.py +705 -103
  54. mcp_ticketer/cli/utils.py +113 -0
  55. mcp_ticketer/core/__init__.py +56 -6
  56. mcp_ticketer/core/adapter.py +533 -2
  57. mcp_ticketer/core/config.py +21 -21
  58. mcp_ticketer/core/exceptions.py +7 -1
  59. mcp_ticketer/core/label_manager.py +732 -0
  60. mcp_ticketer/core/mappers.py +31 -19
  61. mcp_ticketer/core/milestone_manager.py +252 -0
  62. mcp_ticketer/core/models.py +480 -0
  63. mcp_ticketer/core/onepassword_secrets.py +1 -1
  64. mcp_ticketer/core/priority_matcher.py +463 -0
  65. mcp_ticketer/core/project_config.py +132 -14
  66. mcp_ticketer/core/project_utils.py +281 -0
  67. mcp_ticketer/core/project_validator.py +376 -0
  68. mcp_ticketer/core/session_state.py +176 -0
  69. mcp_ticketer/core/state_matcher.py +625 -0
  70. mcp_ticketer/core/url_parser.py +425 -0
  71. mcp_ticketer/core/validators.py +69 -0
  72. mcp_ticketer/mcp/server/__main__.py +2 -1
  73. mcp_ticketer/mcp/server/diagnostic_helper.py +175 -0
  74. mcp_ticketer/mcp/server/main.py +106 -25
  75. mcp_ticketer/mcp/server/routing.py +723 -0
  76. mcp_ticketer/mcp/server/server_sdk.py +58 -0
  77. mcp_ticketer/mcp/server/tools/__init__.py +33 -11
  78. mcp_ticketer/mcp/server/tools/analysis_tools.py +854 -0
  79. mcp_ticketer/mcp/server/tools/attachment_tools.py +5 -5
  80. mcp_ticketer/mcp/server/tools/bulk_tools.py +259 -202
  81. mcp_ticketer/mcp/server/tools/comment_tools.py +74 -12
  82. mcp_ticketer/mcp/server/tools/config_tools.py +1391 -145
  83. mcp_ticketer/mcp/server/tools/diagnostic_tools.py +211 -0
  84. mcp_ticketer/mcp/server/tools/hierarchy_tools.py +870 -460
  85. mcp_ticketer/mcp/server/tools/instruction_tools.py +7 -5
  86. mcp_ticketer/mcp/server/tools/label_tools.py +942 -0
  87. mcp_ticketer/mcp/server/tools/milestone_tools.py +338 -0
  88. mcp_ticketer/mcp/server/tools/pr_tools.py +3 -7
  89. mcp_ticketer/mcp/server/tools/project_status_tools.py +158 -0
  90. mcp_ticketer/mcp/server/tools/project_update_tools.py +473 -0
  91. mcp_ticketer/mcp/server/tools/search_tools.py +209 -97
  92. mcp_ticketer/mcp/server/tools/session_tools.py +308 -0
  93. mcp_ticketer/mcp/server/tools/ticket_tools.py +1107 -124
  94. mcp_ticketer/mcp/server/tools/user_ticket_tools.py +218 -236
  95. mcp_ticketer/queue/queue.py +68 -0
  96. mcp_ticketer/queue/worker.py +1 -1
  97. mcp_ticketer/utils/__init__.py +5 -0
  98. mcp_ticketer/utils/token_utils.py +246 -0
  99. mcp_ticketer-2.2.13.dist-info/METADATA +1396 -0
  100. mcp_ticketer-2.2.13.dist-info/RECORD +158 -0
  101. mcp_ticketer-2.2.13.dist-info/top_level.txt +2 -0
  102. py_mcp_installer/examples/phase3_demo.py +178 -0
  103. py_mcp_installer/scripts/manage_version.py +54 -0
  104. py_mcp_installer/setup.py +6 -0
  105. py_mcp_installer/src/py_mcp_installer/__init__.py +153 -0
  106. py_mcp_installer/src/py_mcp_installer/command_builder.py +445 -0
  107. py_mcp_installer/src/py_mcp_installer/config_manager.py +541 -0
  108. py_mcp_installer/src/py_mcp_installer/exceptions.py +243 -0
  109. py_mcp_installer/src/py_mcp_installer/installation_strategy.py +617 -0
  110. py_mcp_installer/src/py_mcp_installer/installer.py +656 -0
  111. py_mcp_installer/src/py_mcp_installer/mcp_inspector.py +750 -0
  112. py_mcp_installer/src/py_mcp_installer/platform_detector.py +451 -0
  113. py_mcp_installer/src/py_mcp_installer/platforms/__init__.py +26 -0
  114. py_mcp_installer/src/py_mcp_installer/platforms/claude_code.py +225 -0
  115. py_mcp_installer/src/py_mcp_installer/platforms/codex.py +181 -0
  116. py_mcp_installer/src/py_mcp_installer/platforms/cursor.py +191 -0
  117. py_mcp_installer/src/py_mcp_installer/types.py +222 -0
  118. py_mcp_installer/src/py_mcp_installer/utils.py +463 -0
  119. py_mcp_installer/tests/__init__.py +0 -0
  120. py_mcp_installer/tests/platforms/__init__.py +0 -0
  121. py_mcp_installer/tests/test_platform_detector.py +17 -0
  122. mcp_ticketer/adapters/github.py +0 -1574
  123. mcp_ticketer/adapters/jira.py +0 -1258
  124. mcp_ticketer-0.12.0.dist-info/METADATA +0 -550
  125. mcp_ticketer-0.12.0.dist-info/RECORD +0 -91
  126. mcp_ticketer-0.12.0.dist-info/top_level.txt +0 -1
  127. {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.2.13.dist-info}/WHEEL +0 -0
  128. {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.2.13.dist-info}/entry_points.txt +0 -0
  129. {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.2.13.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,545 @@
1
+ """Platform installation commands for mcp-ticketer MCP server.
2
+
3
+ This module provides commands for installing and removing mcp-ticketer
4
+ MCP server configuration for various AI platforms (Claude, Gemini, Codex, Auggie).
5
+ """
6
+
7
+ from pathlib import Path
8
+
9
+ import typer
10
+ from rich.console import Console
11
+ from rich.table import Table
12
+
13
+ # Import for typing compatibility
14
+ from .init_command import init
15
+ from .platform_detection import PlatformDetector, get_platform_by_name
16
+ from .setup_command import _update_mcp_json_credentials
17
+
18
+ console = Console()
19
+
20
+
21
+ def install(
22
+ platform: str | None = typer.Argument(
23
+ None,
24
+ help="Platform to install (claude-code, cursor, gemini, codex, auggie). Use claude-desktop for desktop AI assistant.",
25
+ ),
26
+ auto_detect: bool = typer.Option(
27
+ False,
28
+ "--auto-detect",
29
+ "-d",
30
+ help="Auto-detect and show all code editors (excludes desktop AI assistants by default)",
31
+ ),
32
+ install_all: bool = typer.Option(
33
+ False,
34
+ "--all",
35
+ help="Install for all detected code editors (excludes Claude Desktop unless --include-desktop specified)",
36
+ ),
37
+ include_desktop: bool = typer.Option(
38
+ False,
39
+ "--include-desktop",
40
+ help="Include Claude Desktop in auto-detection and --all installation",
41
+ ),
42
+ adapter: str | None = typer.Option(
43
+ None,
44
+ "--adapter",
45
+ "-a",
46
+ help="Adapter type to use (interactive prompt if not specified)",
47
+ ),
48
+ project_path: str | None = typer.Option(
49
+ None, "--path", help="Project path (default: current directory)"
50
+ ),
51
+ global_config: bool = typer.Option(
52
+ False,
53
+ "--global",
54
+ "-g",
55
+ help="Save to global config instead of project-specific",
56
+ ),
57
+ base_path: str | None = typer.Option(
58
+ None,
59
+ "--base-path",
60
+ "-p",
61
+ help="Base path for ticket storage (AITrackdown only)",
62
+ ),
63
+ api_key: str | None = typer.Option(
64
+ None, "--api-key", help="API key for Linear or API token for JIRA"
65
+ ),
66
+ team_id: str | None = typer.Option(
67
+ None, "--team-id", help="Linear team ID (required for Linear adapter)"
68
+ ),
69
+ jira_server: str | None = typer.Option(
70
+ None,
71
+ "--jira-server",
72
+ help="JIRA server URL (e.g., https://company.atlassian.net)",
73
+ ),
74
+ jira_email: str | None = typer.Option(
75
+ None, "--jira-email", help="JIRA user email for authentication"
76
+ ),
77
+ jira_project: str | None = typer.Option(
78
+ None, "--jira-project", help="Default JIRA project key"
79
+ ),
80
+ github_owner: str | None = typer.Option(
81
+ None, "--github-owner", help="GitHub repository owner"
82
+ ),
83
+ github_repo: str | None = typer.Option(
84
+ None, "--github-repo", help="GitHub repository name"
85
+ ),
86
+ github_token: str | None = typer.Option(
87
+ None, "--github-token", help="GitHub Personal Access Token"
88
+ ),
89
+ dry_run: bool = typer.Option(
90
+ False,
91
+ "--dry-run",
92
+ help="Show what would be done without making changes (for platform installation)",
93
+ ),
94
+ ) -> None:
95
+ """Install MCP server configuration for AI platforms.
96
+
97
+ This command configures mcp-ticketer as an MCP server for various AI
98
+ platforms. It updates platform-specific configuration files to enable
99
+ mcp-ticketer integration.
100
+
101
+ RECOMMENDED: Use 'mcp-ticketer setup' for first-time setup, which
102
+ handles both adapter configuration and platform installation together.
103
+
104
+ Platform Installation:
105
+ # Auto-detect and prompt for platform selection
106
+ mcp-ticketer install
107
+
108
+ # Show all detected platforms
109
+ mcp-ticketer install --auto-detect
110
+
111
+ # Install for all detected platforms
112
+ mcp-ticketer install --all
113
+
114
+ # Install for specific platform
115
+ mcp-ticketer install claude-code # Claude Code (project-level)
116
+ mcp-ticketer install claude-desktop # Claude Desktop (global)
117
+ mcp-ticketer install gemini # Gemini CLI
118
+ mcp-ticketer install codex # Codex
119
+ mcp-ticketer install auggie # Auggie
120
+
121
+ Legacy Usage (adapter setup, deprecated - use 'init' or 'setup' instead):
122
+ mcp-ticketer install --adapter linear # Use 'init' or 'setup' instead
123
+
124
+ """
125
+ detector = PlatformDetector()
126
+
127
+ # Handle auto-detect flag (just show detected platforms and exit)
128
+ if auto_detect:
129
+ detected = detector.detect_all(
130
+ project_path=Path(project_path) if project_path else Path.cwd(),
131
+ exclude_desktop=not include_desktop,
132
+ )
133
+
134
+ if not detected:
135
+ console.print("[yellow]No code editors detected.[/yellow]")
136
+ console.print("\n[bold]Supported code editors:[/bold]")
137
+ console.print(" • Claude Code - Project-level AI code assistant")
138
+ console.print(" • Cursor - AI-powered code editor")
139
+ console.print(" • Auggie - CLI code assistant")
140
+ console.print(" • Codex - CLI code assistant")
141
+ console.print(" • Gemini - CLI code assistant")
142
+ if not include_desktop:
143
+ console.print(
144
+ "\n[dim]Use --include-desktop to also detect Claude Desktop (desktop AI assistant)[/dim]"
145
+ )
146
+ console.print(
147
+ "\n[dim]Install these platforms to use them with mcp-ticketer.[/dim]"
148
+ )
149
+ return
150
+
151
+ console.print("[bold]Detected AI platforms:[/bold]\n")
152
+ table = Table(show_header=True, header_style="bold cyan")
153
+ table.add_column("Platform", style="green")
154
+ table.add_column("Status", style="yellow")
155
+ table.add_column("Scope", style="blue")
156
+ table.add_column("Config Path", style="dim")
157
+
158
+ for plat in detected:
159
+ status = "✓ Installed" if plat.is_installed else "⚠ Config Issue"
160
+ table.add_row(plat.display_name, status, plat.scope, str(plat.config_path))
161
+
162
+ console.print(table)
163
+ console.print(
164
+ "\n[dim]Run 'mcp-ticketer install <platform>' to configure a specific platform[/dim]"
165
+ )
166
+ console.print(
167
+ "[dim]Run 'mcp-ticketer install --all' to configure all detected platforms[/dim]"
168
+ )
169
+ return
170
+
171
+ # Handle --all flag (install for all detected platforms)
172
+ if install_all:
173
+ detected = detector.detect_all(
174
+ project_path=Path(project_path) if project_path else Path.cwd(),
175
+ exclude_desktop=not include_desktop,
176
+ )
177
+
178
+ if not detected:
179
+ console.print("[yellow]No AI platforms detected.[/yellow]")
180
+ console.print(
181
+ "Run 'mcp-ticketer install --auto-detect' to see supported platforms."
182
+ )
183
+ return
184
+
185
+ # Handle dry-run mode - show what would be installed without actually installing
186
+ if dry_run:
187
+ console.print(
188
+ "\n[yellow]DRY RUN - The following platforms would be configured:[/yellow]\n"
189
+ )
190
+
191
+ installable_count = 0
192
+ for plat in detected:
193
+ if plat.is_installed:
194
+ console.print(f" ✓ {plat.display_name} ({plat.scope})")
195
+ installable_count += 1
196
+ else:
197
+ console.print(
198
+ f" ⚠ {plat.display_name} ({plat.scope}) - would be skipped (configuration issue)"
199
+ )
200
+
201
+ console.print(
202
+ f"\n[dim]Would configure {installable_count} platform(s)[/dim]"
203
+ )
204
+ return
205
+
206
+ console.print(
207
+ f"[bold]Installing for {len(detected)} detected platform(s)...[/bold]\n"
208
+ )
209
+
210
+ # Import configuration functions
211
+ from .auggie_configure import configure_auggie_mcp
212
+ from .codex_configure import configure_codex_mcp
213
+ from .cursor_configure import configure_cursor_mcp
214
+ from .gemini_configure import configure_gemini_mcp
215
+ from .mcp_configure import configure_claude_mcp
216
+
217
+ # Map platform names to configuration functions
218
+ platform_mapping = {
219
+ "claude-code": lambda: configure_claude_mcp(
220
+ global_config=False, force=True
221
+ ),
222
+ "claude-desktop": lambda: configure_claude_mcp(
223
+ global_config=True, force=True
224
+ ),
225
+ "cursor": lambda: configure_cursor_mcp(force=True),
226
+ "auggie": lambda: configure_auggie_mcp(force=True),
227
+ "gemini": lambda: configure_gemini_mcp(scope="project", force=True),
228
+ "codex": lambda: configure_codex_mcp(force=True),
229
+ }
230
+
231
+ success_count = 0
232
+ failed = []
233
+
234
+ for plat in detected:
235
+ if not plat.is_installed:
236
+ console.print(
237
+ f"[yellow]⚠[/yellow] Skipping {plat.display_name} (configuration issue)"
238
+ )
239
+ continue
240
+
241
+ config_func = platform_mapping.get(plat.name)
242
+ if not config_func:
243
+ console.print(
244
+ f"[yellow]⚠[/yellow] No installer for {plat.display_name}"
245
+ )
246
+ continue
247
+
248
+ try:
249
+ console.print(f"[cyan]Installing for {plat.display_name}...[/cyan]")
250
+ config_func()
251
+ success_count += 1
252
+
253
+ # Update credentials in parent .mcp.json for Claude platforms
254
+ if plat.name in ("claude-code", "claude-desktop"):
255
+ _update_mcp_json_credentials(Path.cwd(), console)
256
+ except Exception as e:
257
+ console.print(
258
+ f"[red]✗[/red] Failed to install for {plat.display_name}: {e}"
259
+ )
260
+ failed.append(plat.display_name)
261
+
262
+ console.print(
263
+ f"\n[bold]Installation complete:[/bold] {success_count} succeeded"
264
+ )
265
+ if failed:
266
+ console.print(f"[red]Failed:[/red] {', '.join(failed)}")
267
+ return
268
+
269
+ # If no platform argument and no adapter flag, auto-detect and prompt
270
+ if platform is None and adapter is None:
271
+ detected = detector.detect_all(
272
+ project_path=Path(project_path) if project_path else Path.cwd()
273
+ )
274
+
275
+ # Filter to only installed platforms
276
+ installed = [p for p in detected if p.is_installed]
277
+
278
+ if not installed:
279
+ console.print("[yellow]No AI platforms detected.[/yellow]")
280
+ console.print("\n[bold]To see supported platforms:[/bold]")
281
+ console.print(" mcp-ticketer install --auto-detect")
282
+ console.print("\n[bold]Or run legacy adapter setup:[/bold]")
283
+ console.print(" mcp-ticketer install --adapter <adapter-type>")
284
+ return
285
+
286
+ # Show detected platforms and prompt for selection
287
+ console.print("[bold]Detected AI platforms:[/bold]\n")
288
+ for idx, plat in enumerate(installed, 1):
289
+ console.print(f" {idx}. {plat.display_name} ({plat.scope})")
290
+
291
+ console.print(
292
+ "\n[dim]Enter the number of the platform to configure, or 'q' to quit:[/dim]"
293
+ )
294
+ choice = typer.prompt("Select platform")
295
+
296
+ if choice.lower() == "q":
297
+ console.print("Installation cancelled.")
298
+ return
299
+
300
+ try:
301
+ idx = int(choice) - 1
302
+ if idx < 0 or idx >= len(installed):
303
+ console.print("[red]Invalid selection.[/red]")
304
+ raise typer.Exit(1) from None
305
+ platform = installed[idx].name
306
+ except ValueError as e:
307
+ console.print("[red]Invalid input. Please enter a number.[/red]")
308
+ raise typer.Exit(1) from e
309
+
310
+ # If platform argument is provided, handle MCP platform installation (NEW SYNTAX)
311
+ if platform is not None:
312
+ # Validate that the platform is actually installed
313
+ platform_info = get_platform_by_name(
314
+ platform, project_path=Path(project_path) if project_path else Path.cwd()
315
+ )
316
+
317
+ if platform_info and not platform_info.is_installed:
318
+ console.print(
319
+ f"[yellow]⚠[/yellow] {platform_info.display_name} was detected but has a configuration issue."
320
+ )
321
+ console.print(f"[dim]Config path: {platform_info.config_path}[/dim]\n")
322
+
323
+ proceed = typer.confirm(
324
+ "Do you want to proceed with installation anyway?", default=False
325
+ )
326
+ if not proceed:
327
+ console.print("Installation cancelled.")
328
+ return
329
+
330
+ elif not platform_info:
331
+ # Platform not detected at all - warn but allow proceeding
332
+ console.print(
333
+ f"[yellow]⚠[/yellow] Platform '{platform}' not detected on this system."
334
+ )
335
+ console.print(
336
+ "[dim]Run 'mcp-ticketer install --auto-detect' to see detected platforms.[/dim]\n"
337
+ )
338
+
339
+ proceed = typer.confirm(
340
+ "Do you want to proceed with installation anyway?", default=False
341
+ )
342
+ if not proceed:
343
+ console.print("Installation cancelled.")
344
+ return
345
+
346
+ # Import configuration functions
347
+ from .auggie_configure import configure_auggie_mcp
348
+ from .codex_configure import configure_codex_mcp
349
+ from .cursor_configure import configure_cursor_mcp
350
+ from .gemini_configure import configure_gemini_mcp
351
+ from .mcp_configure import configure_claude_mcp
352
+
353
+ # Map platform names to configuration functions
354
+ platform_mapping = {
355
+ "claude-code": {
356
+ "func": lambda: configure_claude_mcp(global_config=False, force=True),
357
+ "name": "Claude Code",
358
+ },
359
+ "claude-desktop": {
360
+ "func": lambda: configure_claude_mcp(global_config=True, force=True),
361
+ "name": "Claude Desktop",
362
+ },
363
+ "cursor": {
364
+ "func": lambda: configure_cursor_mcp(force=True),
365
+ "name": "Cursor",
366
+ },
367
+ "auggie": {
368
+ "func": lambda: configure_auggie_mcp(force=True),
369
+ "name": "Auggie",
370
+ },
371
+ "gemini": {
372
+ "func": lambda: configure_gemini_mcp(scope="project", force=True),
373
+ "name": "Gemini CLI",
374
+ },
375
+ "codex": {
376
+ "func": lambda: configure_codex_mcp(force=True),
377
+ "name": "Codex",
378
+ },
379
+ }
380
+
381
+ if platform not in platform_mapping:
382
+ console.print(f"[red]Unknown platform: {platform}[/red]")
383
+ console.print("\n[bold]Available platforms:[/bold]")
384
+ for p in platform_mapping.keys():
385
+ console.print(f" • {p}")
386
+ raise typer.Exit(1) from None
387
+
388
+ config = platform_mapping[platform]
389
+
390
+ if dry_run:
391
+ console.print(f"[cyan]DRY RUN - Would install for {config['name']}[/cyan]")
392
+ return
393
+
394
+ try:
395
+ config["func"]()
396
+
397
+ # Update credentials in parent .mcp.json for Claude platforms
398
+ if platform in ("claude-code", "claude-desktop"):
399
+ _update_mcp_json_credentials(Path.cwd(), console)
400
+ except Exception as e:
401
+ console.print(f"[red]Installation failed: {e}[/red]")
402
+ raise typer.Exit(1) from e
403
+ return
404
+
405
+ # Otherwise, delegate to init for adapter initialization (LEGACY BEHAVIOR)
406
+ # This makes 'install' and 'init' synonymous when called without platform argument
407
+ init(
408
+ adapter=adapter,
409
+ project_path=project_path,
410
+ global_config=global_config,
411
+ base_path=base_path,
412
+ api_key=api_key,
413
+ team_id=team_id,
414
+ jira_server=jira_server,
415
+ jira_email=jira_email,
416
+ jira_project=jira_project,
417
+ github_owner=github_owner,
418
+ github_repo=github_repo,
419
+ github_token=github_token,
420
+ )
421
+
422
+
423
+ def remove(
424
+ platform: str | None = typer.Argument(
425
+ None,
426
+ help="Platform to remove (claude-code, claude-desktop, cursor, auggie, gemini, codex)",
427
+ ),
428
+ dry_run: bool = typer.Option(
429
+ False, "--dry-run", help="Show what would be done without making changes"
430
+ ),
431
+ ) -> None:
432
+ """Remove mcp-ticketer from AI platforms.
433
+
434
+ Without arguments, shows help and available platforms.
435
+ With a platform argument, removes MCP configuration for that platform.
436
+
437
+ Examples:
438
+ # Remove from Claude Code (project-level)
439
+ mcp-ticketer remove claude-code
440
+
441
+ # Remove from Claude Desktop (global)
442
+ mcp-ticketer remove claude-desktop
443
+
444
+ # Remove from Auggie
445
+ mcp-ticketer remove auggie
446
+
447
+ # Dry run to preview changes
448
+ mcp-ticketer remove claude-code --dry-run
449
+
450
+ """
451
+ # If no platform specified, show help message
452
+ if platform is None:
453
+ console.print("[bold]Remove mcp-ticketer from AI platforms[/bold]\n")
454
+ console.print("Usage: mcp-ticketer remove <platform>\n")
455
+ console.print("[bold]Available platforms:[/bold]")
456
+ console.print(" • claude-code - Claude Code (project-level)")
457
+ console.print(" • claude-desktop - Claude Desktop (global)")
458
+ console.print(" • auggie - Auggie (global)")
459
+ console.print(" • gemini - Gemini CLI (project-level by default)")
460
+ console.print(" • codex - Codex (global)")
461
+ return
462
+
463
+ # Import removal functions
464
+ from .auggie_configure import remove_auggie_mcp
465
+ from .codex_configure import remove_codex_mcp
466
+ from .cursor_configure import remove_cursor_mcp
467
+ from .gemini_configure import remove_gemini_mcp
468
+ from .mcp_configure import remove_claude_mcp
469
+
470
+ # Map platform names to removal functions
471
+ platform_mapping = {
472
+ "claude-code": {
473
+ "func": lambda: remove_claude_mcp(global_config=False, dry_run=dry_run),
474
+ "name": "Claude Code",
475
+ },
476
+ "claude-desktop": {
477
+ "func": lambda: remove_claude_mcp(global_config=True, dry_run=dry_run),
478
+ "name": "Claude Desktop",
479
+ },
480
+ "cursor": {
481
+ "func": lambda: remove_cursor_mcp(dry_run=dry_run),
482
+ "name": "Cursor",
483
+ },
484
+ "auggie": {
485
+ "func": lambda: remove_auggie_mcp(dry_run=dry_run),
486
+ "name": "Auggie",
487
+ },
488
+ "gemini": {
489
+ "func": lambda: remove_gemini_mcp(scope="project", dry_run=dry_run),
490
+ "name": "Gemini CLI",
491
+ },
492
+ "codex": {
493
+ "func": lambda: remove_codex_mcp(dry_run=dry_run),
494
+ "name": "Codex",
495
+ },
496
+ }
497
+
498
+ if platform not in platform_mapping:
499
+ console.print(f"[red]Unknown platform: {platform}[/red]")
500
+ console.print("\n[bold]Available platforms:[/bold]")
501
+ for p in platform_mapping.keys():
502
+ console.print(f" • {p}")
503
+ raise typer.Exit(1) from None
504
+
505
+ config = platform_mapping[platform]
506
+
507
+ try:
508
+ config["func"]()
509
+ except Exception as e:
510
+ console.print(f"[red]Removal failed: {e}[/red]")
511
+ raise typer.Exit(1) from e
512
+
513
+
514
+ def uninstall(
515
+ platform: str | None = typer.Argument(
516
+ None,
517
+ help="Platform to uninstall (claude-code, claude-desktop, auggie, gemini, codex)",
518
+ ),
519
+ dry_run: bool = typer.Option(
520
+ False, "--dry-run", help="Show what would be done without making changes"
521
+ ),
522
+ ) -> None:
523
+ """Uninstall mcp-ticketer from AI platforms (alias for remove).
524
+
525
+ This is an alias for the 'remove' command.
526
+
527
+ Without arguments, shows help and available platforms.
528
+ With a platform argument, removes MCP configuration for that platform.
529
+
530
+ Examples:
531
+ # Uninstall from Claude Code (project-level)
532
+ mcp-ticketer uninstall claude-code
533
+
534
+ # Uninstall from Claude Desktop (global)
535
+ mcp-ticketer uninstall claude-desktop
536
+
537
+ # Uninstall from Auggie
538
+ mcp-ticketer uninstall auggie
539
+
540
+ # Dry run to preview changes
541
+ mcp-ticketer uninstall claude-code --dry-run
542
+
543
+ """
544
+ # Call the remove command with the same parameters
545
+ remove(platform=platform, dry_run=dry_run)