mcp-ticketer 0.3.0__py3-none-any.whl → 2.2.9__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.
Files changed (160) hide show
  1. mcp_ticketer/__init__.py +10 -10
  2. mcp_ticketer/__version__.py +3 -3
  3. mcp_ticketer/_version_scm.py +1 -0
  4. mcp_ticketer/adapters/__init__.py +2 -0
  5. mcp_ticketer/adapters/aitrackdown.py +930 -52
  6. mcp_ticketer/adapters/asana/__init__.py +15 -0
  7. mcp_ticketer/adapters/asana/adapter.py +1537 -0
  8. mcp_ticketer/adapters/asana/client.py +292 -0
  9. mcp_ticketer/adapters/asana/mappers.py +348 -0
  10. mcp_ticketer/adapters/asana/types.py +146 -0
  11. mcp_ticketer/adapters/github/__init__.py +26 -0
  12. mcp_ticketer/adapters/github/adapter.py +3229 -0
  13. mcp_ticketer/adapters/github/client.py +335 -0
  14. mcp_ticketer/adapters/github/mappers.py +797 -0
  15. mcp_ticketer/adapters/github/queries.py +692 -0
  16. mcp_ticketer/adapters/github/types.py +460 -0
  17. mcp_ticketer/adapters/hybrid.py +58 -16
  18. mcp_ticketer/adapters/jira/__init__.py +35 -0
  19. mcp_ticketer/adapters/jira/adapter.py +1351 -0
  20. mcp_ticketer/adapters/jira/client.py +271 -0
  21. mcp_ticketer/adapters/jira/mappers.py +246 -0
  22. mcp_ticketer/adapters/jira/queries.py +216 -0
  23. mcp_ticketer/adapters/jira/types.py +304 -0
  24. mcp_ticketer/adapters/linear/__init__.py +1 -1
  25. mcp_ticketer/adapters/linear/adapter.py +3810 -462
  26. mcp_ticketer/adapters/linear/client.py +312 -69
  27. mcp_ticketer/adapters/linear/mappers.py +305 -85
  28. mcp_ticketer/adapters/linear/queries.py +317 -17
  29. mcp_ticketer/adapters/linear/types.py +187 -64
  30. mcp_ticketer/adapters/linear.py +2 -2
  31. mcp_ticketer/analysis/__init__.py +56 -0
  32. mcp_ticketer/analysis/dependency_graph.py +255 -0
  33. mcp_ticketer/analysis/health_assessment.py +304 -0
  34. mcp_ticketer/analysis/orphaned.py +218 -0
  35. mcp_ticketer/analysis/project_status.py +594 -0
  36. mcp_ticketer/analysis/similarity.py +224 -0
  37. mcp_ticketer/analysis/staleness.py +266 -0
  38. mcp_ticketer/automation/__init__.py +11 -0
  39. mcp_ticketer/automation/project_updates.py +378 -0
  40. mcp_ticketer/cache/memory.py +9 -8
  41. mcp_ticketer/cli/adapter_diagnostics.py +91 -54
  42. mcp_ticketer/cli/auggie_configure.py +116 -15
  43. mcp_ticketer/cli/codex_configure.py +274 -82
  44. mcp_ticketer/cli/configure.py +1323 -151
  45. mcp_ticketer/cli/cursor_configure.py +314 -0
  46. mcp_ticketer/cli/diagnostics.py +209 -114
  47. mcp_ticketer/cli/discover.py +297 -26
  48. mcp_ticketer/cli/gemini_configure.py +119 -26
  49. mcp_ticketer/cli/init_command.py +880 -0
  50. mcp_ticketer/cli/install_mcp_server.py +418 -0
  51. mcp_ticketer/cli/instruction_commands.py +435 -0
  52. mcp_ticketer/cli/linear_commands.py +256 -130
  53. mcp_ticketer/cli/main.py +140 -1544
  54. mcp_ticketer/cli/mcp_configure.py +1013 -100
  55. mcp_ticketer/cli/mcp_server_commands.py +415 -0
  56. mcp_ticketer/cli/migrate_config.py +12 -8
  57. mcp_ticketer/cli/platform_commands.py +123 -0
  58. mcp_ticketer/cli/platform_detection.py +477 -0
  59. mcp_ticketer/cli/platform_installer.py +545 -0
  60. mcp_ticketer/cli/project_update_commands.py +350 -0
  61. mcp_ticketer/cli/python_detection.py +126 -0
  62. mcp_ticketer/cli/queue_commands.py +15 -15
  63. mcp_ticketer/cli/setup_command.py +794 -0
  64. mcp_ticketer/cli/simple_health.py +84 -59
  65. mcp_ticketer/cli/ticket_commands.py +1375 -0
  66. mcp_ticketer/cli/update_checker.py +313 -0
  67. mcp_ticketer/cli/utils.py +195 -72
  68. mcp_ticketer/core/__init__.py +64 -1
  69. mcp_ticketer/core/adapter.py +618 -18
  70. mcp_ticketer/core/config.py +77 -68
  71. mcp_ticketer/core/env_discovery.py +75 -16
  72. mcp_ticketer/core/env_loader.py +121 -97
  73. mcp_ticketer/core/exceptions.py +32 -24
  74. mcp_ticketer/core/http_client.py +26 -26
  75. mcp_ticketer/core/instructions.py +405 -0
  76. mcp_ticketer/core/label_manager.py +732 -0
  77. mcp_ticketer/core/mappers.py +42 -30
  78. mcp_ticketer/core/milestone_manager.py +252 -0
  79. mcp_ticketer/core/models.py +566 -19
  80. mcp_ticketer/core/onepassword_secrets.py +379 -0
  81. mcp_ticketer/core/priority_matcher.py +463 -0
  82. mcp_ticketer/core/project_config.py +189 -49
  83. mcp_ticketer/core/project_utils.py +281 -0
  84. mcp_ticketer/core/project_validator.py +376 -0
  85. mcp_ticketer/core/registry.py +3 -3
  86. mcp_ticketer/core/session_state.py +176 -0
  87. mcp_ticketer/core/state_matcher.py +592 -0
  88. mcp_ticketer/core/url_parser.py +425 -0
  89. mcp_ticketer/core/validators.py +69 -0
  90. mcp_ticketer/defaults/ticket_instructions.md +644 -0
  91. mcp_ticketer/mcp/__init__.py +29 -1
  92. mcp_ticketer/mcp/__main__.py +60 -0
  93. mcp_ticketer/mcp/server/__init__.py +25 -0
  94. mcp_ticketer/mcp/server/__main__.py +60 -0
  95. mcp_ticketer/mcp/server/constants.py +58 -0
  96. mcp_ticketer/mcp/server/diagnostic_helper.py +175 -0
  97. mcp_ticketer/mcp/server/dto.py +195 -0
  98. mcp_ticketer/mcp/server/main.py +1343 -0
  99. mcp_ticketer/mcp/server/response_builder.py +206 -0
  100. mcp_ticketer/mcp/server/routing.py +723 -0
  101. mcp_ticketer/mcp/server/server_sdk.py +151 -0
  102. mcp_ticketer/mcp/server/tools/__init__.py +69 -0
  103. mcp_ticketer/mcp/server/tools/analysis_tools.py +854 -0
  104. mcp_ticketer/mcp/server/tools/attachment_tools.py +224 -0
  105. mcp_ticketer/mcp/server/tools/bulk_tools.py +330 -0
  106. mcp_ticketer/mcp/server/tools/comment_tools.py +152 -0
  107. mcp_ticketer/mcp/server/tools/config_tools.py +1564 -0
  108. mcp_ticketer/mcp/server/tools/diagnostic_tools.py +211 -0
  109. mcp_ticketer/mcp/server/tools/hierarchy_tools.py +942 -0
  110. mcp_ticketer/mcp/server/tools/instruction_tools.py +295 -0
  111. mcp_ticketer/mcp/server/tools/label_tools.py +942 -0
  112. mcp_ticketer/mcp/server/tools/milestone_tools.py +338 -0
  113. mcp_ticketer/mcp/server/tools/pr_tools.py +150 -0
  114. mcp_ticketer/mcp/server/tools/project_status_tools.py +158 -0
  115. mcp_ticketer/mcp/server/tools/project_update_tools.py +473 -0
  116. mcp_ticketer/mcp/server/tools/search_tools.py +318 -0
  117. mcp_ticketer/mcp/server/tools/session_tools.py +308 -0
  118. mcp_ticketer/mcp/server/tools/ticket_tools.py +1413 -0
  119. mcp_ticketer/mcp/server/tools/user_ticket_tools.py +364 -0
  120. mcp_ticketer/queue/__init__.py +1 -0
  121. mcp_ticketer/queue/health_monitor.py +168 -136
  122. mcp_ticketer/queue/manager.py +78 -63
  123. mcp_ticketer/queue/queue.py +108 -21
  124. mcp_ticketer/queue/run_worker.py +2 -2
  125. mcp_ticketer/queue/ticket_registry.py +213 -155
  126. mcp_ticketer/queue/worker.py +96 -58
  127. mcp_ticketer/utils/__init__.py +5 -0
  128. mcp_ticketer/utils/token_utils.py +246 -0
  129. mcp_ticketer-2.2.9.dist-info/METADATA +1396 -0
  130. mcp_ticketer-2.2.9.dist-info/RECORD +158 -0
  131. mcp_ticketer-2.2.9.dist-info/top_level.txt +2 -0
  132. py_mcp_installer/examples/phase3_demo.py +178 -0
  133. py_mcp_installer/scripts/manage_version.py +54 -0
  134. py_mcp_installer/setup.py +6 -0
  135. py_mcp_installer/src/py_mcp_installer/__init__.py +153 -0
  136. py_mcp_installer/src/py_mcp_installer/command_builder.py +445 -0
  137. py_mcp_installer/src/py_mcp_installer/config_manager.py +541 -0
  138. py_mcp_installer/src/py_mcp_installer/exceptions.py +243 -0
  139. py_mcp_installer/src/py_mcp_installer/installation_strategy.py +617 -0
  140. py_mcp_installer/src/py_mcp_installer/installer.py +656 -0
  141. py_mcp_installer/src/py_mcp_installer/mcp_inspector.py +750 -0
  142. py_mcp_installer/src/py_mcp_installer/platform_detector.py +451 -0
  143. py_mcp_installer/src/py_mcp_installer/platforms/__init__.py +26 -0
  144. py_mcp_installer/src/py_mcp_installer/platforms/claude_code.py +225 -0
  145. py_mcp_installer/src/py_mcp_installer/platforms/codex.py +181 -0
  146. py_mcp_installer/src/py_mcp_installer/platforms/cursor.py +191 -0
  147. py_mcp_installer/src/py_mcp_installer/types.py +222 -0
  148. py_mcp_installer/src/py_mcp_installer/utils.py +463 -0
  149. py_mcp_installer/tests/__init__.py +0 -0
  150. py_mcp_installer/tests/platforms/__init__.py +0 -0
  151. py_mcp_installer/tests/test_platform_detector.py +17 -0
  152. mcp_ticketer/adapters/github.py +0 -1354
  153. mcp_ticketer/adapters/jira.py +0 -1011
  154. mcp_ticketer/mcp/server.py +0 -2030
  155. mcp_ticketer-0.3.0.dist-info/METADATA +0 -414
  156. mcp_ticketer-0.3.0.dist-info/RECORD +0 -59
  157. mcp_ticketer-0.3.0.dist-info/top_level.txt +0 -1
  158. {mcp_ticketer-0.3.0.dist-info → mcp_ticketer-2.2.9.dist-info}/WHEEL +0 -0
  159. {mcp_ticketer-0.3.0.dist-info → mcp_ticketer-2.2.9.dist-info}/entry_points.txt +0 -0
  160. {mcp_ticketer-0.3.0.dist-info → mcp_ticketer-2.2.9.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,415 @@
1
+ """MCP server management commands for mcp-ticketer."""
2
+
3
+ import json
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ import typer
8
+ from rich.console import Console
9
+
10
+ console = Console()
11
+
12
+ # Create MCP configuration command group
13
+ mcp_app = typer.Typer(
14
+ name="mcp",
15
+ help="Configure MCP integration for AI clients (Claude, Gemini, Codex, Auggie)",
16
+ add_completion=False,
17
+ invoke_without_command=True,
18
+ )
19
+
20
+
21
+ @mcp_app.callback()
22
+ def mcp_callback(
23
+ ctx: typer.Context,
24
+ project_path: str | None = typer.Option(
25
+ None, "--path", "-p", help="Project directory path (default: current directory)"
26
+ ),
27
+ ) -> None:
28
+ """MCP command group - runs MCP server if no subcommand provided.
29
+
30
+ Examples:
31
+ --------
32
+ mcp-ticketer mcp # Start server in current directory
33
+ mcp-ticketer mcp --path /dir # Start server in specific directory
34
+ mcp-ticketer mcp -p /dir # Start server (short form)
35
+ mcp-ticketer mcp status # Check MCP status
36
+ mcp-ticketer mcp serve # Explicitly start server
37
+
38
+ """
39
+ if ctx.invoked_subcommand is None:
40
+ # No subcommand provided, run the serve command
41
+ # Change to project directory if provided
42
+ if project_path:
43
+ import os
44
+
45
+ os.chdir(project_path)
46
+ # Invoke the serve command through context
47
+ ctx.invoke(mcp_serve, adapter=None, base_path=None)
48
+
49
+
50
+ @mcp_app.command(name="serve")
51
+ def mcp_serve(
52
+ adapter: str | None = typer.Option(
53
+ None, "--adapter", "-a", help="Override default adapter type"
54
+ ),
55
+ base_path: str | None = typer.Option(
56
+ None, "--base-path", help="Base path for AITrackdown adapter"
57
+ ),
58
+ ) -> None:
59
+ """Start MCP server for JSON-RPC communication over stdio.
60
+
61
+ This command is used by Claude Code/Desktop when connecting to the MCP server.
62
+ You typically don't need to run this manually - use 'mcp-ticketer install add' to configure.
63
+
64
+ Configuration Resolution:
65
+ - When MCP server starts, it uses the current working directory (cwd)
66
+ - The cwd is set by Claude Code/Desktop from the 'cwd' field in .mcp/config.json
67
+ - Configuration is loaded with this priority:
68
+ 1. Project-specific: .mcp-ticketer/config.json in cwd
69
+ 2. Global: ~/.mcp-ticketer/config.json
70
+ 3. Default: aitrackdown adapter with .aitrackdown base path
71
+ """
72
+ # Local imports to avoid circular dependency
73
+ from ..mcp.server.server_sdk import configure_adapter
74
+ from ..mcp.server.server_sdk import main as sdk_main
75
+
76
+ # Import load_config locally to avoid circular import
77
+ # (main.py imports this module, so we can't import from main at module level)
78
+ from .ticket_commands import load_config
79
+
80
+ # Load configuration (respects project-specific config in cwd)
81
+ config = load_config()
82
+
83
+ # Determine adapter type with priority: CLI arg > config > .env files > default
84
+ if adapter:
85
+ # Priority 1: Command line argument
86
+ adapter_type = adapter
87
+ # Get base config from config file
88
+ adapters_config = config.get("adapters", {})
89
+ adapter_config = adapters_config.get(adapter_type, {})
90
+ else:
91
+ # Priority 2: Configuration file (project-specific)
92
+ adapter_type = config.get("default_adapter")
93
+ if adapter_type:
94
+ adapters_config = config.get("adapters", {})
95
+ adapter_config = adapters_config.get(adapter_type, {})
96
+ else:
97
+ # Priority 3: .env files (auto-detection fallback)
98
+ from ..mcp.server.main import _load_env_configuration
99
+
100
+ env_config = _load_env_configuration()
101
+ if env_config:
102
+ adapter_type = env_config["adapter_type"]
103
+ adapter_config = env_config["adapter_config"]
104
+ else:
105
+ # Priority 4: Default fallback
106
+ adapter_type = "aitrackdown"
107
+ adapters_config = config.get("adapters", {})
108
+ adapter_config = adapters_config.get(adapter_type, {})
109
+
110
+ # Override with command line options if provided (highest priority)
111
+ if base_path and adapter_type == "aitrackdown":
112
+ adapter_config["base_path"] = base_path
113
+
114
+ # Fallback to legacy config format
115
+ if not adapter_config and "config" in config:
116
+ adapter_config = config["config"]
117
+
118
+ # MCP server uses stdio for JSON-RPC, so we can't print to stdout
119
+ # Only print to stderr to avoid interfering with the protocol
120
+ if sys.stderr.isatty():
121
+ # Only print if stderr is a terminal (not redirected)
122
+ console.file = sys.stderr
123
+ console.print(
124
+ f"[green]Starting MCP SDK server[/green] with {adapter_type} adapter"
125
+ )
126
+ console.print(
127
+ "[dim]Server running on stdio. Send JSON-RPC requests via stdin.[/dim]"
128
+ )
129
+
130
+ # Configure adapter and run SDK server
131
+ try:
132
+ configure_adapter(adapter_type, adapter_config)
133
+ sdk_main()
134
+ except KeyboardInterrupt:
135
+ # Send this to stderr
136
+ if sys.stderr.isatty():
137
+ console.print("\n[yellow]Server stopped by user[/yellow]")
138
+ sys.exit(0)
139
+ except Exception as e:
140
+ # Log error to stderr
141
+ sys.stderr.write(f"MCP server error: {e}\n")
142
+ sys.exit(1)
143
+
144
+
145
+ @mcp_app.command(name="claude")
146
+ def mcp_claude(
147
+ global_config: bool = typer.Option(
148
+ False,
149
+ "--global",
150
+ "-g",
151
+ help="Configure Claude Desktop instead of project-level",
152
+ ),
153
+ force: bool = typer.Option(
154
+ False, "--force", "-f", help="Overwrite existing configuration"
155
+ ),
156
+ ) -> None:
157
+ """Configure Claude Code to use mcp-ticketer MCP server.
158
+
159
+ Reads configuration from .mcp-ticketer/config.json and updates
160
+ Claude Code's MCP settings accordingly.
161
+
162
+ By default, configures project-level (.mcp/config.json).
163
+ Use --global to configure Claude Desktop instead.
164
+
165
+ Examples:
166
+ --------
167
+ # Configure for current project (default)
168
+ mcp-ticketer mcp claude
169
+
170
+ # Configure Claude Desktop globally
171
+ mcp-ticketer mcp claude --global
172
+
173
+ # Force overwrite existing configuration
174
+ mcp-ticketer mcp claude --force
175
+
176
+ """
177
+ from ..cli.mcp_configure import configure_claude_mcp
178
+
179
+ try:
180
+ configure_claude_mcp(global_config=global_config, force=force)
181
+ except Exception as e:
182
+ console.print(f"[red]✗ Configuration failed:[/red] {e}")
183
+ raise typer.Exit(1) from e
184
+
185
+
186
+ @mcp_app.command(name="gemini")
187
+ def mcp_gemini(
188
+ scope: str = typer.Option(
189
+ "project",
190
+ "--scope",
191
+ "-s",
192
+ help="Configuration scope: 'project' (default) or 'user'",
193
+ ),
194
+ force: bool = typer.Option(
195
+ False, "--force", "-f", help="Overwrite existing configuration"
196
+ ),
197
+ ) -> None:
198
+ """Configure Gemini CLI to use mcp-ticketer MCP server.
199
+
200
+ Reads configuration from .mcp-ticketer/config.json and creates
201
+ Gemini CLI settings file with mcp-ticketer configuration.
202
+
203
+ By default, configures project-level (.gemini/settings.json).
204
+ Use --scope user to configure user-level (~/.gemini/settings.json).
205
+
206
+ Examples:
207
+ --------
208
+ # Configure for current project (default)
209
+ mcp-ticketer mcp gemini
210
+
211
+ # Configure at user level
212
+ mcp-ticketer mcp gemini --scope user
213
+
214
+ # Force overwrite existing configuration
215
+ mcp-ticketer mcp gemini --force
216
+
217
+ """
218
+ from ..cli.gemini_configure import configure_gemini_mcp
219
+
220
+ # Validate scope parameter
221
+ if scope not in ["project", "user"]:
222
+ console.print(
223
+ f"[red]✗ Invalid scope:[/red] '{scope}'. Must be 'project' or 'user'"
224
+ )
225
+ raise typer.Exit(1) from None
226
+
227
+ try:
228
+ configure_gemini_mcp(scope=scope, force=force) # type: ignore
229
+ except Exception as e:
230
+ console.print(f"[red]✗ Configuration failed:[/red] {e}")
231
+ raise typer.Exit(1) from e
232
+
233
+
234
+ @mcp_app.command(name="codex")
235
+ def mcp_codex(
236
+ force: bool = typer.Option(
237
+ False, "--force", "-f", help="Overwrite existing configuration"
238
+ ),
239
+ ) -> None:
240
+ """Configure Codex CLI to use mcp-ticketer MCP server.
241
+
242
+ Reads configuration from .mcp-ticketer/config.json and creates
243
+ Codex CLI config.toml with mcp-ticketer configuration.
244
+
245
+ IMPORTANT: Codex CLI ONLY supports global configuration at ~/.codex/config.toml.
246
+ There is no project-level configuration support. After configuration,
247
+ you must restart Codex CLI for changes to take effect.
248
+
249
+ Examples:
250
+ --------
251
+ # Configure Codex CLI globally
252
+ mcp-ticketer mcp codex
253
+
254
+ # Force overwrite existing configuration
255
+ mcp-ticketer mcp codex --force
256
+
257
+ """
258
+ from ..cli.codex_configure import configure_codex_mcp
259
+
260
+ try:
261
+ configure_codex_mcp(force=force)
262
+ except Exception as e:
263
+ console.print(f"[red]✗ Configuration failed:[/red] {e}")
264
+ raise typer.Exit(1) from e
265
+
266
+
267
+ @mcp_app.command(name="auggie")
268
+ def mcp_auggie(
269
+ force: bool = typer.Option(
270
+ False, "--force", "-f", help="Overwrite existing configuration"
271
+ ),
272
+ ) -> None:
273
+ """Configure Auggie CLI to use mcp-ticketer MCP server.
274
+
275
+ Reads configuration from .mcp-ticketer/config.json and creates
276
+ Auggie CLI settings.json with mcp-ticketer configuration.
277
+
278
+ IMPORTANT: Auggie CLI ONLY supports global configuration at ~/.augment/settings.json.
279
+ There is no project-level configuration support. After configuration,
280
+ you must restart Auggie CLI for changes to take effect.
281
+
282
+ Examples:
283
+ --------
284
+ # Configure Auggie CLI globally
285
+ mcp-ticketer mcp auggie
286
+
287
+ # Force overwrite existing configuration
288
+ mcp-ticketer mcp auggie --force
289
+
290
+ """
291
+ from ..cli.auggie_configure import configure_auggie_mcp
292
+
293
+ try:
294
+ configure_auggie_mcp(force=force)
295
+ except Exception as e:
296
+ console.print(f"[red]✗ Configuration failed:[/red] {e}")
297
+ raise typer.Exit(1) from e
298
+
299
+
300
+ @mcp_app.command(name="status")
301
+ def mcp_status() -> None:
302
+ """Check MCP server status.
303
+
304
+ Shows whether the MCP server is configured and running for various platforms.
305
+
306
+ Examples:
307
+ --------
308
+ mcp-ticketer mcp status
309
+
310
+ """
311
+ console.print("[bold]MCP Server Status[/bold]\n")
312
+
313
+ # Check project-level configuration
314
+ project_config = Path.cwd() / ".mcp-ticketer" / "config.json"
315
+ if project_config.exists():
316
+ console.print(f"[green]✓[/green] Project config found: {project_config}")
317
+ try:
318
+ with open(project_config) as f:
319
+ config = json.load(f)
320
+ adapter = config.get("default_adapter", "aitrackdown")
321
+ console.print(f" Default adapter: [cyan]{adapter}[/cyan]")
322
+ except Exception as e:
323
+ console.print(f" [yellow]Warning: Could not read config: {e}[/yellow]")
324
+ else:
325
+ console.print("[yellow]○[/yellow] No project config found")
326
+
327
+ # Check Claude Code configuration
328
+ claude_code_config = Path.cwd() / ".mcp" / "config.json"
329
+ if claude_code_config.exists():
330
+ console.print(
331
+ f"\n[green]✓[/green] Claude Code configured: {claude_code_config}"
332
+ )
333
+ else:
334
+ console.print("\n[yellow]○[/yellow] Claude Code not configured")
335
+
336
+ # Check Claude Desktop configuration
337
+ claude_desktop_config = (
338
+ Path.home()
339
+ / "Library"
340
+ / "Application Support"
341
+ / "Claude"
342
+ / "claude_desktop_config.json"
343
+ )
344
+ if claude_desktop_config.exists():
345
+ try:
346
+ with open(claude_desktop_config) as f:
347
+ config = json.load(f)
348
+ if "mcpServers" in config and "mcp-ticketer" in config["mcpServers"]:
349
+ console.print(
350
+ f"[green]✓[/green] Claude Desktop configured: {claude_desktop_config}"
351
+ )
352
+ else:
353
+ console.print(
354
+ "[yellow]○[/yellow] Claude Desktop config exists but mcp-ticketer not found"
355
+ )
356
+ except Exception:
357
+ console.print(
358
+ "[yellow]○[/yellow] Claude Desktop config exists but could not be read"
359
+ )
360
+ else:
361
+ console.print("[yellow]○[/yellow] Claude Desktop not configured")
362
+
363
+ # Check Gemini configuration
364
+ gemini_project_config = Path.cwd() / ".gemini" / "settings.json"
365
+ gemini_user_config = Path.home() / ".gemini" / "settings.json"
366
+ if gemini_project_config.exists():
367
+ console.print(
368
+ f"\n[green]✓[/green] Gemini (project) configured: {gemini_project_config}"
369
+ )
370
+ elif gemini_user_config.exists():
371
+ console.print(
372
+ f"\n[green]✓[/green] Gemini (user) configured: {gemini_user_config}"
373
+ )
374
+ else:
375
+ console.print("\n[yellow]○[/yellow] Gemini not configured")
376
+
377
+ # Check Codex configuration
378
+ codex_config = Path.home() / ".codex" / "config.toml"
379
+ if codex_config.exists():
380
+ console.print(f"[green]✓[/green] Codex configured: {codex_config}")
381
+ else:
382
+ console.print("[yellow]○[/yellow] Codex not configured")
383
+
384
+ # Check Auggie configuration
385
+ auggie_config = Path.home() / ".augment" / "settings.json"
386
+ if auggie_config.exists():
387
+ console.print(f"[green]✓[/green] Auggie configured: {auggie_config}")
388
+ else:
389
+ console.print("[yellow]○[/yellow] Auggie not configured")
390
+
391
+ console.print(
392
+ "\n[dim]Run 'mcp-ticketer install <platform>' to configure a platform[/dim]"
393
+ )
394
+
395
+
396
+ @mcp_app.command(name="stop")
397
+ def mcp_stop() -> None:
398
+ """Stop MCP server (placeholder - MCP runs on-demand via stdio).
399
+
400
+ Note: The MCP server runs on-demand when AI clients connect via stdio.
401
+ It doesn't run as a persistent background service, so there's nothing to stop.
402
+ This command is provided for consistency but has no effect.
403
+
404
+ Examples:
405
+ --------
406
+ mcp-ticketer mcp stop
407
+
408
+ """
409
+ console.print(
410
+ "[yellow]ℹ[/yellow] MCP server runs on-demand via stdio (not as a background service)"
411
+ )
412
+ console.print("There is no persistent server process to stop.")
413
+ console.print(
414
+ "\n[dim]The server starts automatically when AI clients connect and stops when they disconnect.[/dim]"
415
+ )
@@ -22,14 +22,17 @@ def migrate_config_command(dry_run: bool = False) -> None:
22
22
  """
23
23
  resolver = ConfigResolver()
24
24
 
25
+ # Get project config path (project-local only for security)
26
+ project_config_path = resolver.project_path / resolver.PROJECT_CONFIG_SUBPATH
27
+
25
28
  # Check if old config exists
26
- if not resolver.GLOBAL_CONFIG_PATH.exists():
29
+ if not project_config_path.exists():
27
30
  console.print("[yellow]No configuration found to migrate[/yellow]")
28
31
  return
29
32
 
30
33
  # Load old config
31
34
  try:
32
- with open(resolver.GLOBAL_CONFIG_PATH) as f:
35
+ with open(project_config_path) as f:
33
36
  old_config = json.load(f)
34
37
  except Exception as e:
35
38
  console.print(f"[red]Failed to load config: {e}[/red]")
@@ -71,22 +74,23 @@ def migrate_config_command(dry_run: bool = False) -> None:
71
74
  return
72
75
 
73
76
  # Backup old config
74
- backup_path = resolver.GLOBAL_CONFIG_PATH.with_suffix(".json.bak")
77
+ project_config_path = resolver.project_path / resolver.PROJECT_CONFIG_SUBPATH
78
+ backup_path = project_config_path.with_suffix(".json.bak")
75
79
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
76
- backup_path = resolver.GLOBAL_CONFIG_PATH.parent / f"config.{timestamp}.bak"
80
+ backup_path = project_config_path.parent / f"config.{timestamp}.bak"
77
81
 
78
82
  try:
79
- shutil.copy(resolver.GLOBAL_CONFIG_PATH, backup_path)
83
+ shutil.copy(project_config_path, backup_path)
80
84
  console.print(f"[green]✓[/green] Backed up old config to: {backup_path}")
81
85
  except Exception as e:
82
86
  console.print(f"[red]Failed to backup config: {e}[/red]")
83
87
  return
84
88
 
85
- # Save new config
89
+ # Save new config (to project-local config)
86
90
  try:
87
- resolver.save_global_config(new_config)
91
+ resolver.save_project_config(new_config)
88
92
  console.print("[green]✓[/green] Migration complete!")
89
- console.print(f"[dim]New config saved to: {resolver.GLOBAL_CONFIG_PATH}[/dim]")
93
+ console.print(f"[dim]New config saved to: {project_config_path}[/dim]")
90
94
  except Exception as e:
91
95
  console.print(f"[red]Failed to save new config: {e}[/red]")
92
96
  console.print(f"[yellow]Old config backed up at: {backup_path}[/yellow]")
@@ -0,0 +1,123 @@
1
+ """Platform-specific command groups."""
2
+
3
+ import typer
4
+
5
+ # Import platform-specific command modules
6
+ from .linear_commands import app as linear_app
7
+
8
+ # Create main platform command group
9
+ app = typer.Typer(
10
+ name="platform",
11
+ help="Platform-specific commands (Linear, JIRA, GitHub, AITrackdown)",
12
+ )
13
+
14
+ # Register Linear commands
15
+ app.add_typer(linear_app, name="linear")
16
+
17
+ # Create placeholder apps for other platforms
18
+
19
+ # JIRA platform commands (placeholder)
20
+ jira_app = typer.Typer(
21
+ name="jira",
22
+ help="JIRA-specific workspace and project management",
23
+ )
24
+
25
+
26
+ @jira_app.command("projects")
27
+ def jira_list_projects() -> None:
28
+ """List JIRA projects (placeholder - not yet implemented)."""
29
+ from rich.console import Console
30
+
31
+ console = Console()
32
+ console.print("[yellow]JIRA platform commands are not yet implemented.[/yellow]")
33
+ console.print(
34
+ "Use the generic ticket commands for JIRA operations:\n"
35
+ " mcp-ticketer ticket create 'My ticket'\n"
36
+ " mcp-ticketer ticket list"
37
+ )
38
+
39
+
40
+ @jira_app.command("configure")
41
+ def jira_configure() -> None:
42
+ """Configure JIRA adapter (placeholder - not yet implemented)."""
43
+ from rich.console import Console
44
+
45
+ console = Console()
46
+ console.print("[yellow]JIRA platform commands are not yet implemented.[/yellow]")
47
+ console.print("Use 'mcp-ticketer init --adapter jira' to configure JIRA adapter.")
48
+
49
+
50
+ # GitHub platform commands (placeholder)
51
+ github_app = typer.Typer(
52
+ name="github",
53
+ help="GitHub-specific repository and issue management",
54
+ )
55
+
56
+
57
+ @github_app.command("repos")
58
+ def github_list_repos() -> None:
59
+ """List GitHub repositories (placeholder - not yet implemented)."""
60
+ from rich.console import Console
61
+
62
+ console = Console()
63
+ console.print("[yellow]GitHub platform commands are not yet implemented.[/yellow]")
64
+ console.print(
65
+ "Use the generic ticket commands for GitHub operations:\n"
66
+ " mcp-ticketer ticket create 'My issue'\n"
67
+ " mcp-ticketer ticket list"
68
+ )
69
+
70
+
71
+ @github_app.command("configure")
72
+ def github_configure() -> None:
73
+ """Configure GitHub adapter (placeholder - not yet implemented)."""
74
+ from rich.console import Console
75
+
76
+ console = Console()
77
+ console.print("[yellow]GitHub platform commands are not yet implemented.[/yellow]")
78
+ console.print(
79
+ "Use 'mcp-ticketer init --adapter github' to configure GitHub adapter."
80
+ )
81
+
82
+
83
+ # AITrackdown platform commands (placeholder)
84
+ aitrackdown_app = typer.Typer(
85
+ name="aitrackdown",
86
+ help="AITrackdown-specific local file management",
87
+ )
88
+
89
+
90
+ @aitrackdown_app.command("info")
91
+ def aitrackdown_info() -> None:
92
+ """Show AITrackdown storage information (placeholder - not yet implemented)."""
93
+ from rich.console import Console
94
+
95
+ console = Console()
96
+ console.print(
97
+ "[yellow]AITrackdown platform commands are not yet implemented.[/yellow]"
98
+ )
99
+ console.print(
100
+ "Use the generic ticket commands for AITrackdown operations:\n"
101
+ " mcp-ticketer ticket create 'My ticket'\n"
102
+ " mcp-ticketer ticket list"
103
+ )
104
+
105
+
106
+ @aitrackdown_app.command("configure")
107
+ def aitrackdown_configure() -> None:
108
+ """Configure AITrackdown adapter (placeholder - not yet implemented)."""
109
+ from rich.console import Console
110
+
111
+ console = Console()
112
+ console.print(
113
+ "[yellow]AITrackdown platform commands are not yet implemented.[/yellow]"
114
+ )
115
+ console.print(
116
+ "Use 'mcp-ticketer init --adapter aitrackdown' to configure AITrackdown adapter."
117
+ )
118
+
119
+
120
+ # Register all platform command groups
121
+ app.add_typer(jira_app, name="jira")
122
+ app.add_typer(github_app, name="github")
123
+ app.add_typer(aitrackdown_app, name="aitrackdown")