mcp-ticketer 0.2.0__py3-none-any.whl → 0.3.0__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.
@@ -1,6 +1,6 @@
1
1
  """Version information for mcp-ticketer package."""
2
2
 
3
- __version__ = "0.2.0"
3
+ __version__ = "0.3.0"
4
4
  __version_info__ = tuple(int(part) for part in __version__.split("."))
5
5
 
6
6
  # Package metadata
@@ -0,0 +1,384 @@
1
+ """Adapter diagnostics and configuration validation."""
2
+
3
+ import os
4
+ import sys
5
+ from pathlib import Path
6
+ from typing import Any, Dict, List, Optional, Tuple
7
+
8
+ from rich.console import Console
9
+ from rich.panel import Panel
10
+ from rich.table import Table
11
+ from rich.text import Text
12
+
13
+ from ..core import AdapterRegistry
14
+ from ..core.env_discovery import discover_config
15
+
16
+
17
+ def diagnose_adapter_configuration(console: Console) -> None:
18
+ """Diagnose adapter configuration and provide recommendations.
19
+
20
+ Args:
21
+ console: Rich console for output
22
+ """
23
+ console.print("\n[bold blue]🔍 MCP Ticketer Adapter Configuration Diagnostics[/bold blue]\n")
24
+
25
+ # 1. Check .env files
26
+ _check_env_files(console)
27
+
28
+ # 2. Check configuration files
29
+ _check_configuration_files(console)
30
+
31
+ # 3. Check adapter discovery
32
+ _check_adapter_discovery(console)
33
+
34
+ # 4. Test adapter instantiation
35
+ _test_adapter_instantiation(console)
36
+
37
+ # 5. Provide recommendations
38
+ _provide_recommendations(console)
39
+
40
+
41
+ def _check_env_files(console: Console) -> None:
42
+ """Check .env files for configuration."""
43
+ console.print("[bold]1. .env File Configuration[/bold]")
44
+
45
+ # Load .env files
46
+ from ..mcp.server import _load_env_configuration
47
+ env_config = _load_env_configuration()
48
+
49
+ # Check for .env files
50
+ env_files = [".env.local", ".env"]
51
+
52
+ table = Table(show_header=True, header_style="bold magenta")
53
+ table.add_column("File", style="cyan")
54
+ table.add_column("Status", style="green")
55
+ table.add_column("Variables Found", style="yellow")
56
+
57
+ for env_file in env_files:
58
+ env_path = Path.cwd() / env_file
59
+ if env_path.exists():
60
+ try:
61
+ # Count variables in file
62
+ var_count = 0
63
+ with open(env_path, 'r') as f:
64
+ for line in f:
65
+ line = line.strip()
66
+ if line and not line.startswith('#') and '=' in line:
67
+ var_count += 1
68
+
69
+ status = "✅ Found"
70
+ variables = f"{var_count} variables"
71
+ except Exception:
72
+ status = "⚠️ Error reading"
73
+ variables = "Unknown"
74
+ else:
75
+ status = "❌ Missing"
76
+ variables = "N/A"
77
+
78
+ table.add_row(env_file, status, variables)
79
+
80
+ console.print(table)
81
+
82
+ # Show discovered configuration
83
+ if env_config:
84
+ console.print(f"\n[green]✅ Discovered adapter: {env_config['adapter_type']}[/green]")
85
+ config_keys = list(env_config['adapter_config'].keys())
86
+ console.print(f"[dim]Configuration keys: {config_keys}[/dim]")
87
+ else:
88
+ console.print("\n[yellow]⚠️ No adapter configuration found in .env files[/yellow]")
89
+
90
+ console.print()
91
+
92
+
93
+ def _check_configuration_files(console: Console) -> None:
94
+ """Check configuration files."""
95
+ console.print("[bold]2. Configuration Files[/bold]")
96
+
97
+ config_files = [
98
+ (".env.local", "Local environment file (highest priority)"),
99
+ (".env", "Environment file"),
100
+ (".mcp-ticketer/config.json", "Project configuration"),
101
+ (str(Path.home() / ".mcp-ticketer" / "config.json"), "Global configuration"),
102
+ ]
103
+
104
+ table = Table(show_header=True, header_style="bold magenta")
105
+ table.add_column("File", style="cyan")
106
+ table.add_column("Description", style="white")
107
+ table.add_column("Status", style="green")
108
+ table.add_column("Size", style="yellow")
109
+
110
+ for file_path, description in config_files:
111
+ path = Path(file_path)
112
+ if path.exists():
113
+ try:
114
+ size = path.stat().st_size
115
+ status = "✅ Found"
116
+ size_str = f"{size} bytes"
117
+ except Exception:
118
+ status = "⚠️ Error"
119
+ size_str = "Unknown"
120
+ else:
121
+ status = "❌ Missing"
122
+ size_str = "N/A"
123
+
124
+ table.add_row(str(path), description, status, size_str)
125
+
126
+ console.print(table)
127
+ console.print()
128
+
129
+
130
+ def _check_adapter_discovery(console: Console) -> None:
131
+ """Check adapter discovery from configuration."""
132
+ console.print("[bold]3. Adapter Discovery[/bold]")
133
+
134
+ try:
135
+ # Discover configuration
136
+ discovered = discover_config(Path.cwd())
137
+
138
+ if discovered and discovered.adapters:
139
+ primary = discovered.get_primary_adapter()
140
+
141
+ table = Table(show_header=True, header_style="bold magenta")
142
+ table.add_column("Adapter", style="cyan")
143
+ table.add_column("Confidence", style="white")
144
+ table.add_column("Source", style="green")
145
+ table.add_column("Status", style="yellow")
146
+
147
+ for adapter_info in discovered.adapters:
148
+ confidence = f"{adapter_info.confidence:.0%}"
149
+ status = "✅ Primary" if adapter_info == primary else "⚪ Available"
150
+
151
+ table.add_row(
152
+ adapter_info.adapter_type,
153
+ confidence,
154
+ adapter_info.found_in,
155
+ status
156
+ )
157
+
158
+ console.print(table)
159
+
160
+ if primary:
161
+ console.print(f"\n[green]✅ Primary adapter detected: {primary.adapter_type}[/green]")
162
+ console.print(f"[dim]Source: {primary.found_in}[/dim]")
163
+ console.print(f"[dim]Confidence: {primary.confidence:.0%}[/dim]")
164
+ else:
165
+ console.print("\n[yellow]⚠️ No primary adapter detected[/yellow]")
166
+ else:
167
+ console.print("[red]❌ No adapters discovered[/red]")
168
+ console.print("[dim]This usually means no credentials are configured[/dim]")
169
+
170
+ except Exception as e:
171
+ console.print(f"[red]❌ Error during discovery: {e}[/red]")
172
+
173
+ console.print()
174
+
175
+
176
+ def _test_adapter_instantiation(console: Console) -> None:
177
+ """Test adapter instantiation."""
178
+ console.print("[bold]4. Adapter Instantiation Test[/bold]")
179
+
180
+ # Determine which adapter to test from .env files
181
+ from ..mcp.server import _load_env_configuration
182
+ env_config = _load_env_configuration()
183
+
184
+ if env_config:
185
+ adapter_type = env_config["adapter_type"]
186
+ config = env_config["adapter_config"]
187
+ else:
188
+ # Try to discover from existing discovery system
189
+ try:
190
+ discovered = discover_config(Path.cwd())
191
+ if discovered and discovered.adapters:
192
+ primary = discovered.get_primary_adapter()
193
+ if primary:
194
+ adapter_type = primary.adapter_type
195
+ # Build config from discovery
196
+ from ..mcp.server import _build_adapter_config_from_env_vars
197
+ config = _build_adapter_config_from_env_vars(adapter_type, {})
198
+ else:
199
+ adapter_type = "aitrackdown"
200
+ config = {"base_path": ".aitrackdown"}
201
+ else:
202
+ adapter_type = "aitrackdown"
203
+ config = {"base_path": ".aitrackdown"}
204
+ except Exception:
205
+ adapter_type = "aitrackdown"
206
+ config = {"base_path": ".aitrackdown"}
207
+
208
+ console.print(f"Testing adapter: [cyan]{adapter_type}[/cyan]")
209
+ console.print(f"Configuration keys: [yellow]{list(config.keys())}[/yellow]")
210
+
211
+ try:
212
+ # Try to instantiate adapter
213
+ adapter = AdapterRegistry.get_adapter(adapter_type, config)
214
+
215
+ console.print(f"[green]✅ Adapter instantiated successfully: {adapter.__class__.__name__}[/green]")
216
+
217
+ # Test basic functionality
218
+ if hasattr(adapter, 'validate_credentials'):
219
+ try:
220
+ is_valid, error_msg = adapter.validate_credentials()
221
+ if is_valid:
222
+ console.print("[green]✅ Credentials validation passed[/green]")
223
+ else:
224
+ console.print(f"[red]❌ Credentials validation failed: {error_msg}[/red]")
225
+ except Exception as e:
226
+ console.print(f"[yellow]⚠️ Credentials validation error: {e}[/yellow]")
227
+
228
+ except Exception as e:
229
+ console.print(f"[red]❌ Adapter instantiation failed: {e}[/red]")
230
+
231
+ # Provide specific guidance based on adapter type
232
+ if adapter_type == "linear":
233
+ console.print("\n[yellow]Linear adapter requires in .env/.env.local:[/yellow]")
234
+ console.print("• LINEAR_API_KEY=your_api_key")
235
+ console.print("• LINEAR_TEAM_ID=your_team_id (or LINEAR_TEAM_KEY=your_team_key)")
236
+ elif adapter_type == "github":
237
+ console.print("\n[yellow]GitHub adapter requires in .env/.env.local:[/yellow]")
238
+ console.print("• GITHUB_TOKEN=your_token")
239
+ console.print("• GITHUB_OWNER=your_username")
240
+ console.print("• GITHUB_REPO=your_repository")
241
+ elif adapter_type == "jira":
242
+ console.print("\n[yellow]JIRA adapter requires in .env/.env.local:[/yellow]")
243
+ console.print("• JIRA_SERVER=your_server_url")
244
+ console.print("• JIRA_EMAIL=your_email")
245
+ console.print("• JIRA_API_TOKEN=your_token")
246
+
247
+ console.print()
248
+
249
+
250
+ def _provide_recommendations(console: Console) -> None:
251
+ """Provide configuration recommendations."""
252
+ console.print("[bold]5. Recommendations[/bold]")
253
+
254
+ # Check .env configuration
255
+ from ..mcp.server import _load_env_configuration
256
+ env_config = _load_env_configuration()
257
+
258
+ recommendations = []
259
+
260
+ if not env_config:
261
+ recommendations.append(
262
+ "Create .env.local or .env file with adapter configuration"
263
+ )
264
+ recommendations.append(
265
+ "Add MCP_TICKETER_ADAPTER=linear (or github, jira) to specify adapter type"
266
+ )
267
+ else:
268
+ adapter_type = env_config["adapter_type"]
269
+ config = env_config["adapter_config"]
270
+
271
+ # Check for incomplete configurations
272
+ if adapter_type == "linear":
273
+ if not config.get("api_key"):
274
+ recommendations.append("Add LINEAR_API_KEY to .env file")
275
+ if not config.get("team_id") and not config.get("team_key"):
276
+ recommendations.append("Add LINEAR_TEAM_ID or LINEAR_TEAM_KEY to .env file")
277
+
278
+ elif adapter_type == "github":
279
+ missing = []
280
+ if not config.get("token"):
281
+ missing.append("GITHUB_TOKEN")
282
+ if not config.get("owner"):
283
+ missing.append("GITHUB_OWNER")
284
+ if not config.get("repo"):
285
+ missing.append("GITHUB_REPO")
286
+ if missing:
287
+ recommendations.append(f"Add missing GitHub variables to .env: {', '.join(missing)}")
288
+
289
+ elif adapter_type == "jira":
290
+ missing = []
291
+ if not config.get("server"):
292
+ missing.append("JIRA_SERVER")
293
+ if not config.get("email"):
294
+ missing.append("JIRA_EMAIL")
295
+ if not config.get("api_token"):
296
+ missing.append("JIRA_API_TOKEN")
297
+ if missing:
298
+ recommendations.append(f"Add missing JIRA variables to .env: {', '.join(missing)}")
299
+
300
+ if recommendations:
301
+ for i, rec in enumerate(recommendations, 1):
302
+ console.print(f"{i}. [yellow]{rec}[/yellow]")
303
+ else:
304
+ console.print("[green]✅ Configuration looks good![/green]")
305
+
306
+ # Show .env file examples
307
+ console.print("\n[bold].env File Examples:[/bold]")
308
+ console.print("• Linear: [cyan]echo 'MCP_TICKETER_ADAPTER=linear\\nLINEAR_API_KEY=your_key\\nLINEAR_TEAM_ID=your_team' > .env.local[/cyan]")
309
+ console.print("• GitHub: [cyan]echo 'MCP_TICKETER_ADAPTER=github\\nGITHUB_TOKEN=your_token\\nGITHUB_OWNER=user\\nGITHUB_REPO=repo' > .env.local[/cyan]")
310
+
311
+ console.print("\n[bold]Quick Setup Commands:[/bold]")
312
+ console.print("• For Linear: [cyan]mcp-ticketer init linear[/cyan]")
313
+ console.print("• For GitHub: [cyan]mcp-ticketer init github[/cyan]")
314
+ console.print("• For JIRA: [cyan]mcp-ticketer init jira[/cyan]")
315
+ console.print("• For local files: [cyan]mcp-ticketer init aitrackdown[/cyan]")
316
+
317
+ console.print("\n[bold]Test Configuration:[/bold]")
318
+ console.print("• Run diagnostics: [cyan]mcp-ticketer diagnose[/cyan]")
319
+ console.print("• Test ticket creation: [cyan]mcp-ticketer create 'Test ticket'[/cyan]")
320
+ console.print("• List tickets: [cyan]mcp-ticketer list[/cyan]")
321
+
322
+
323
+ def get_adapter_status() -> Dict[str, Any]:
324
+ """Get current adapter status for programmatic use.
325
+
326
+ Returns:
327
+ Dictionary with adapter status information
328
+ """
329
+ status = {
330
+ "adapter_type": None,
331
+ "configuration_source": None,
332
+ "credentials_valid": False,
333
+ "error_message": None,
334
+ "recommendations": []
335
+ }
336
+
337
+ try:
338
+ # Check .env files first
339
+ from ..mcp.server import _load_env_configuration
340
+ env_config = _load_env_configuration()
341
+
342
+ if env_config:
343
+ adapter_type = env_config["adapter_type"]
344
+ config = env_config["adapter_config"]
345
+ status["configuration_source"] = ".env files"
346
+ else:
347
+ # Try discovery system
348
+ discovered = discover_config(Path.cwd())
349
+ if discovered and discovered.adapters:
350
+ primary = discovered.get_primary_adapter()
351
+ if primary:
352
+ adapter_type = primary.adapter_type
353
+ status["configuration_source"] = primary.found_in
354
+ # Build basic config
355
+ from ..mcp.server import _build_adapter_config_from_env_vars
356
+ config = _build_adapter_config_from_env_vars(adapter_type, {})
357
+ else:
358
+ adapter_type = "aitrackdown"
359
+ config = {"base_path": ".aitrackdown"}
360
+ status["configuration_source"] = "default"
361
+ else:
362
+ adapter_type = "aitrackdown"
363
+ config = {"base_path": ".aitrackdown"}
364
+ status["configuration_source"] = "default"
365
+
366
+ status["adapter_type"] = adapter_type
367
+
368
+ # Test adapter instantiation
369
+ adapter = AdapterRegistry.get_adapter(adapter_type, config)
370
+
371
+ # Test credentials if possible
372
+ if hasattr(adapter, 'validate_credentials'):
373
+ is_valid, error_msg = adapter.validate_credentials()
374
+ status["credentials_valid"] = is_valid
375
+ if not is_valid:
376
+ status["error_message"] = error_msg
377
+ else:
378
+ status["credentials_valid"] = True # Assume valid if no validation method
379
+
380
+ except Exception as e:
381
+ status["error_message"] = str(e)
382
+ status["recommendations"].append("Check .env file configuration and credentials")
383
+
384
+ return status
mcp_ticketer/cli/main.py CHANGED
@@ -317,13 +317,161 @@ def get_adapter(
317
317
  return AdapterRegistry.get_adapter(adapter_type, adapter_config)
318
318
 
319
319
 
320
+ def _prompt_for_adapter_selection(console: Console) -> str:
321
+ """Interactive prompt for adapter selection.
322
+
323
+ Args:
324
+ console: Rich console for output
325
+
326
+ Returns:
327
+ Selected adapter type
328
+ """
329
+ console.print("\n[bold blue]🚀 MCP Ticketer Setup[/bold blue]")
330
+ console.print("Choose which ticket system you want to connect to:\n")
331
+
332
+ # Define adapter options with descriptions
333
+ adapters = [
334
+ {
335
+ "name": "linear",
336
+ "title": "Linear",
337
+ "description": "Modern project management (linear.app)",
338
+ "requirements": "API key and team ID"
339
+ },
340
+ {
341
+ "name": "github",
342
+ "title": "GitHub Issues",
343
+ "description": "GitHub repository issues",
344
+ "requirements": "Personal access token, owner, and repo"
345
+ },
346
+ {
347
+ "name": "jira",
348
+ "title": "JIRA",
349
+ "description": "Atlassian JIRA project management",
350
+ "requirements": "Server URL, email, and API token"
351
+ },
352
+ {
353
+ "name": "aitrackdown",
354
+ "title": "Local Files (AITrackdown)",
355
+ "description": "Store tickets in local files (no external service)",
356
+ "requirements": "None - works offline"
357
+ }
358
+ ]
359
+
360
+ # Display options
361
+ for i, adapter in enumerate(adapters, 1):
362
+ console.print(f"[cyan]{i}.[/cyan] [bold]{adapter['title']}[/bold]")
363
+ console.print(f" {adapter['description']}")
364
+ console.print(f" [dim]Requirements: {adapter['requirements']}[/dim]\n")
365
+
366
+ # Get user selection
367
+ while True:
368
+ try:
369
+ choice = typer.prompt(
370
+ "Select adapter (1-4)",
371
+ type=int,
372
+ default=1
373
+ )
374
+ if 1 <= choice <= len(adapters):
375
+ selected_adapter = adapters[choice - 1]
376
+ console.print(f"\n[green]✓ Selected: {selected_adapter['title']}[/green]")
377
+ return selected_adapter["name"]
378
+ else:
379
+ console.print(f"[red]Please enter a number between 1 and {len(adapters)}[/red]")
380
+ except (ValueError, typer.Abort):
381
+ console.print("[yellow]Setup cancelled.[/yellow]")
382
+ raise typer.Exit(0)
383
+
384
+
385
+ @app.command()
386
+ def setup(
387
+ adapter: Optional[str] = typer.Option(
388
+ None,
389
+ "--adapter",
390
+ "-a",
391
+ help="Adapter type to use (interactive prompt if not specified)",
392
+ ),
393
+ project_path: Optional[str] = typer.Option(
394
+ None, "--path", help="Project path (default: current directory)"
395
+ ),
396
+ global_config: bool = typer.Option(
397
+ False,
398
+ "--global",
399
+ "-g",
400
+ help="Save to global config instead of project-specific",
401
+ ),
402
+ base_path: Optional[str] = typer.Option(
403
+ None,
404
+ "--base-path",
405
+ "-p",
406
+ help="Base path for ticket storage (AITrackdown only)",
407
+ ),
408
+ api_key: Optional[str] = typer.Option(
409
+ None, "--api-key", help="API key for Linear or API token for JIRA"
410
+ ),
411
+ team_id: Optional[str] = typer.Option(
412
+ None, "--team-id", help="Linear team ID (required for Linear adapter)"
413
+ ),
414
+ jira_server: Optional[str] = typer.Option(
415
+ None,
416
+ "--jira-server",
417
+ help="JIRA server URL (e.g., https://company.atlassian.net)",
418
+ ),
419
+ jira_email: Optional[str] = typer.Option(
420
+ None, "--jira-email", help="JIRA user email for authentication"
421
+ ),
422
+ jira_project: Optional[str] = typer.Option(
423
+ None, "--jira-project", help="Default JIRA project key"
424
+ ),
425
+ github_owner: Optional[str] = typer.Option(
426
+ None, "--github-owner", help="GitHub repository owner"
427
+ ),
428
+ github_repo: Optional[str] = typer.Option(
429
+ None, "--github-repo", help="GitHub repository name"
430
+ ),
431
+ github_token: Optional[str] = typer.Option(
432
+ None, "--github-token", help="GitHub Personal Access Token"
433
+ ),
434
+ ) -> None:
435
+ """Interactive setup wizard for MCP Ticketer (alias for init).
436
+
437
+ This command provides a user-friendly setup experience with prompts
438
+ to guide you through configuring MCP Ticketer for your preferred
439
+ ticket management system. It's identical to 'init' and 'install'.
440
+
441
+ Examples:
442
+ # Run interactive setup
443
+ mcp-ticketer setup
444
+
445
+ # Setup with specific adapter
446
+ mcp-ticketer setup --adapter linear
447
+
448
+ # Setup for different project
449
+ mcp-ticketer setup --path /path/to/project
450
+ """
451
+ # Call init with all parameters
452
+ init(
453
+ adapter=adapter,
454
+ project_path=project_path,
455
+ global_config=global_config,
456
+ base_path=base_path,
457
+ api_key=api_key,
458
+ team_id=team_id,
459
+ jira_server=jira_server,
460
+ jira_email=jira_email,
461
+ jira_project=jira_project,
462
+ github_owner=github_owner,
463
+ github_repo=github_repo,
464
+ github_token=github_token,
465
+ )
466
+
467
+
320
468
  @app.command()
321
469
  def init(
322
470
  adapter: Optional[str] = typer.Option(
323
471
  None,
324
472
  "--adapter",
325
473
  "-a",
326
- help="Adapter type to use (auto-detected from .env if not specified)",
474
+ help="Adapter type to use (interactive prompt if not specified)",
327
475
  ),
328
476
  project_path: Optional[str] = typer.Option(
329
477
  None, "--path", help="Project path (default: current directory)"
@@ -369,11 +517,17 @@ def init(
369
517
  ) -> None:
370
518
  """Initialize mcp-ticketer for the current project.
371
519
 
520
+ This command sets up MCP Ticketer configuration with interactive prompts
521
+ to guide you through the process. It auto-detects adapter configuration
522
+ from .env files or prompts for interactive setup if no configuration is found.
523
+
372
524
  Creates .mcp-ticketer/config.json in the current directory with
373
525
  auto-detected or specified adapter configuration.
374
526
 
527
+ Note: 'setup' and 'install' are synonyms for this command.
528
+
375
529
  Examples:
376
- # Auto-detect from .env.local
530
+ # Interactive setup (same as 'setup' and 'install')
377
531
  mcp-ticketer init
378
532
 
379
533
  # Force specific adapter
@@ -429,16 +583,21 @@ def init(
429
583
  f"\n[dim]Configuration found in: {primary.found_in}[/dim]"
430
584
  )
431
585
  console.print(f"[dim]Confidence: {primary.confidence:.0%}[/dim]")
586
+
587
+ # Ask user to confirm auto-detected adapter
588
+ if not typer.confirm(
589
+ f"Use detected {adapter_type} adapter?",
590
+ default=True,
591
+ ):
592
+ adapter_type = None # Will trigger interactive selection
432
593
  else:
433
- adapter_type = "aitrackdown" # Fallback
434
- console.print(
435
- "[yellow]⚠ No credentials found, defaulting to aitrackdown[/yellow]"
436
- )
594
+ adapter_type = None # Will trigger interactive selection
437
595
  else:
438
- adapter_type = "aitrackdown" # Fallback
439
- console.print(
440
- "[yellow]⚠ No .env files found, defaulting to aitrackdown[/yellow]"
441
- )
596
+ adapter_type = None # Will trigger interactive selection
597
+
598
+ # If no adapter determined, show interactive selection
599
+ if not adapter_type:
600
+ adapter_type = _prompt_for_adapter_selection(console)
442
601
 
443
602
  # 2. Create configuration based on adapter type
444
603
  config = {"default_adapter": adapter_type, "adapters": {}}
@@ -462,59 +621,99 @@ def init(
462
621
  }
463
622
 
464
623
  elif adapter_type == "linear":
465
- # If not auto-discovered, build from CLI params
624
+ # If not auto-discovered, build from CLI params or prompt
466
625
  if adapter_type not in config["adapters"]:
467
626
  linear_config = {}
468
627
 
469
- # Team ID
470
- if team_id:
471
- linear_config["team_id"] = team_id
472
-
473
628
  # API Key
474
629
  linear_api_key = api_key or os.getenv("LINEAR_API_KEY")
630
+ if not linear_api_key and not discovered:
631
+ console.print("\n[bold]Linear Configuration[/bold]")
632
+ console.print("You need a Linear API key to connect to Linear.")
633
+ console.print("[dim]Get your API key at: https://linear.app/settings/api[/dim]\n")
634
+
635
+ linear_api_key = typer.prompt(
636
+ "Enter your Linear API key",
637
+ hide_input=True
638
+ )
639
+
475
640
  if linear_api_key:
476
641
  linear_config["api_key"] = linear_api_key
477
- elif not discovered:
478
- console.print("[yellow]Warning:[/yellow] No Linear API key provided.")
479
- console.print(
480
- "Set LINEAR_API_KEY environment variable or use --api-key option"
481
- )
482
642
 
483
- if linear_config:
484
- linear_config["type"] = "linear"
485
- config["adapters"]["linear"] = linear_config
643
+ # Team ID
644
+ linear_team_id = team_id or os.getenv("LINEAR_TEAM_ID")
645
+ if not linear_team_id and not discovered:
646
+ console.print("\nYou need your Linear team ID.")
647
+ console.print("[dim]Find it in Linear settings or team URL[/dim]\n")
648
+
649
+ linear_team_id = typer.prompt("Enter your Linear team ID")
650
+
651
+ if linear_team_id:
652
+ linear_config["team_id"] = linear_team_id
653
+
654
+ if not linear_config.get("api_key") or not linear_config.get("team_id"):
655
+ console.print("[red]Error:[/red] Linear requires both API key and team ID")
656
+ console.print("Run 'mcp-ticketer init --adapter linear' with proper credentials")
657
+ raise typer.Exit(1)
658
+
659
+ linear_config["type"] = "linear"
660
+ config["adapters"]["linear"] = linear_config
486
661
 
487
662
  elif adapter_type == "jira":
488
- # If not auto-discovered, build from CLI params
663
+ # If not auto-discovered, build from CLI params or prompt
489
664
  if adapter_type not in config["adapters"]:
490
665
  server = jira_server or os.getenv("JIRA_SERVER")
491
666
  email = jira_email or os.getenv("JIRA_EMAIL")
492
667
  token = api_key or os.getenv("JIRA_API_TOKEN")
493
668
  project = jira_project or os.getenv("JIRA_PROJECT_KEY")
494
669
 
670
+ # Interactive prompts for missing values
671
+ if not server and not discovered:
672
+ console.print("\n[bold]JIRA Configuration[/bold]")
673
+ console.print("Enter your JIRA server details.\n")
674
+
675
+ server = typer.prompt(
676
+ "JIRA server URL (e.g., https://company.atlassian.net)"
677
+ )
678
+
679
+ if not email and not discovered:
680
+ email = typer.prompt("Your JIRA email address")
681
+
682
+ if not token and not discovered:
683
+ console.print("\nYou need a JIRA API token.")
684
+ console.print("[dim]Generate one at: https://id.atlassian.com/manage/api-tokens[/dim]\n")
685
+
686
+ token = typer.prompt(
687
+ "Enter your JIRA API token",
688
+ hide_input=True
689
+ )
690
+
691
+ if not project and not discovered:
692
+ project = typer.prompt(
693
+ "Default JIRA project key (optional, press Enter to skip)",
694
+ default="",
695
+ show_default=False
696
+ )
697
+
698
+ # Validate required fields
495
699
  if not server:
496
700
  console.print("[red]Error:[/red] JIRA server URL is required")
497
- console.print(
498
- "Use --jira-server or set JIRA_SERVER environment variable"
499
- )
500
701
  raise typer.Exit(1)
501
702
 
502
703
  if not email:
503
704
  console.print("[red]Error:[/red] JIRA email is required")
504
- console.print("Use --jira-email or set JIRA_EMAIL environment variable")
505
705
  raise typer.Exit(1)
506
706
 
507
707
  if not token:
508
708
  console.print("[red]Error:[/red] JIRA API token is required")
509
- console.print(
510
- "Use --api-key or set JIRA_API_TOKEN environment variable"
511
- )
512
- console.print(
513
- "[dim]Generate token at: https://id.atlassian.com/manage/api-tokens[/dim]"
514
- )
515
709
  raise typer.Exit(1)
516
710
 
517
- jira_config = {"server": server, "email": email, "api_token": token}
711
+ jira_config = {
712
+ "server": server,
713
+ "email": email,
714
+ "api_token": token,
715
+ "type": "jira"
716
+ }
518
717
 
519
718
  if project:
520
719
  jira_config["project_key"] = project
@@ -522,45 +721,50 @@ def init(
522
721
  config["adapters"]["jira"] = jira_config
523
722
 
524
723
  elif adapter_type == "github":
525
- # If not auto-discovered, build from CLI params
724
+ # If not auto-discovered, build from CLI params or prompt
526
725
  if adapter_type not in config["adapters"]:
527
726
  owner = github_owner or os.getenv("GITHUB_OWNER")
528
727
  repo = github_repo or os.getenv("GITHUB_REPO")
529
728
  token = github_token or os.getenv("GITHUB_TOKEN")
530
729
 
730
+ # Interactive prompts for missing values
731
+ if not owner and not discovered:
732
+ console.print("\n[bold]GitHub Configuration[/bold]")
733
+ console.print("Enter your GitHub repository details.\n")
734
+
735
+ owner = typer.prompt("GitHub repository owner (username or organization)")
736
+
737
+ if not repo and not discovered:
738
+ repo = typer.prompt("GitHub repository name")
739
+
740
+ if not token and not discovered:
741
+ console.print("\nYou need a GitHub Personal Access Token.")
742
+ console.print("[dim]Create one at: https://github.com/settings/tokens/new[/dim]")
743
+ console.print("[dim]Required scopes: repo (for private repos) or public_repo (for public repos)[/dim]\n")
744
+
745
+ token = typer.prompt(
746
+ "Enter your GitHub Personal Access Token",
747
+ hide_input=True
748
+ )
749
+
750
+ # Validate required fields
531
751
  if not owner:
532
752
  console.print("[red]Error:[/red] GitHub repository owner is required")
533
- console.print(
534
- "Use --github-owner or set GITHUB_OWNER environment variable"
535
- )
536
753
  raise typer.Exit(1)
537
754
 
538
755
  if not repo:
539
756
  console.print("[red]Error:[/red] GitHub repository name is required")
540
- console.print(
541
- "Use --github-repo or set GITHUB_REPO environment variable"
542
- )
543
757
  raise typer.Exit(1)
544
758
 
545
759
  if not token:
546
- console.print(
547
- "[red]Error:[/red] GitHub Personal Access Token is required"
548
- )
549
- console.print(
550
- "Use --github-token or set GITHUB_TOKEN environment variable"
551
- )
552
- console.print(
553
- "[dim]Create token at: https://github.com/settings/tokens/new[/dim]"
554
- )
555
- console.print(
556
- "[dim]Required scopes: repo (for private repos) or public_repo (for public repos)[/dim]"
557
- )
760
+ console.print("[red]Error:[/red] GitHub Personal Access Token is required")
558
761
  raise typer.Exit(1)
559
762
 
560
763
  config["adapters"]["github"] = {
561
764
  "owner": owner,
562
765
  "repo": repo,
563
766
  "token": token,
767
+ "type": "github"
564
768
  }
565
769
 
566
770
  # 5. Save to appropriate location
@@ -600,6 +804,48 @@ def init(
600
804
  f.write("# MCP Ticketer\n.mcp-ticketer/\n")
601
805
  console.print("[dim]✓ Created .gitignore with .mcp-ticketer/[/dim]")
602
806
 
807
+ # Show next steps
808
+ _show_next_steps(console, adapter_type, config_file_path)
809
+
810
+
811
+ def _show_next_steps(console: Console, adapter_type: str, config_file_path: Path) -> None:
812
+ """Show helpful next steps after initialization.
813
+
814
+ Args:
815
+ console: Rich console for output
816
+ adapter_type: Type of adapter that was configured
817
+ config_file_path: Path to the configuration file
818
+ """
819
+ console.print("\n[bold green]🎉 Setup Complete![/bold green]")
820
+ console.print(f"MCP Ticketer is now configured to use {adapter_type.title()}.\n")
821
+
822
+ console.print("[bold]Next Steps:[/bold]")
823
+ console.print("1. [cyan]Test your configuration:[/cyan]")
824
+ console.print(" mcp-ticketer diagnose")
825
+ console.print("\n2. [cyan]Create a test ticket:[/cyan]")
826
+ console.print(" mcp-ticketer create 'Test ticket from MCP Ticketer'")
827
+
828
+ if adapter_type != "aitrackdown":
829
+ console.print(f"\n3. [cyan]Verify the ticket appears in {adapter_type.title()}[/cyan]")
830
+
831
+ if adapter_type == "linear":
832
+ console.print(" Check your Linear workspace for the new ticket")
833
+ elif adapter_type == "github":
834
+ console.print(" Check your GitHub repository's Issues tab")
835
+ elif adapter_type == "jira":
836
+ console.print(" Check your JIRA project for the new ticket")
837
+ else:
838
+ console.print("\n3. [cyan]Check local ticket storage:[/cyan]")
839
+ console.print(" ls .aitrackdown/")
840
+
841
+ console.print("\n4. [cyan]Configure MCP clients (optional):[/cyan]")
842
+ console.print(" mcp-ticketer mcp claude # For Claude Code")
843
+ console.print(" mcp-ticketer mcp auggie # For Auggie")
844
+ console.print(" mcp-ticketer mcp gemini # For Gemini CLI")
845
+
846
+ console.print(f"\n[dim]Configuration saved to: {config_file_path}[/dim]")
847
+ console.print("[dim]Run 'mcp-ticketer --help' for more commands[/dim]")
848
+
603
849
 
604
850
  @app.command()
605
851
  def install(
@@ -653,12 +899,12 @@ def install(
653
899
  ) -> None:
654
900
  """Initialize mcp-ticketer for the current project (alias for init).
655
901
 
656
- This command is synonymous with 'init' and provides the same functionality.
657
- Creates .mcp-ticketer/config.json in the current directory with
658
- auto-detected or specified adapter configuration.
902
+ This command is synonymous with 'init' and 'setup' - all three provide
903
+ identical functionality with interactive prompts to guide you through
904
+ configuring MCP Ticketer for your preferred ticket management system.
659
905
 
660
906
  Examples:
661
- # Auto-detect from .env.local
907
+ # Interactive setup (same as 'init' and 'setup')
662
908
  mcp-ticketer install
663
909
 
664
910
  # Force specific adapter
@@ -1548,6 +1794,9 @@ def check(queue_id: str = typer.Argument(..., help="Queue ID to check")):
1548
1794
  console.print(f"\nRetry Count: {item.retry_count}")
1549
1795
 
1550
1796
 
1797
+
1798
+
1799
+
1551
1800
  @app.command()
1552
1801
  def serve(
1553
1802
  adapter: Optional[AdapterType] = typer.Option(
@@ -1575,16 +1824,27 @@ def serve(
1575
1824
  # Load configuration (respects project-specific config in cwd)
1576
1825
  config = load_config()
1577
1826
 
1578
- # Determine adapter type
1579
- adapter_type = (
1580
- adapter.value if adapter else config.get("default_adapter", "aitrackdown")
1581
- )
1582
-
1583
- # Get adapter configuration
1584
- adapters_config = config.get("adapters", {})
1585
- adapter_config = adapters_config.get(adapter_type, {})
1827
+ # Determine adapter type with priority: CLI arg > .env files > config > default
1828
+ if adapter:
1829
+ # Priority 1: Command line argument
1830
+ adapter_type = adapter.value
1831
+ # Get base config from config file
1832
+ adapters_config = config.get("adapters", {})
1833
+ adapter_config = adapters_config.get(adapter_type, {})
1834
+ else:
1835
+ # Priority 2: .env files
1836
+ from ..mcp.server import _load_env_configuration
1837
+ env_config = _load_env_configuration()
1838
+ if env_config:
1839
+ adapter_type = env_config["adapter_type"]
1840
+ adapter_config = env_config["adapter_config"]
1841
+ else:
1842
+ # Priority 3: Configuration file
1843
+ adapter_type = config.get("default_adapter", "aitrackdown")
1844
+ adapters_config = config.get("adapters", {})
1845
+ adapter_config = adapters_config.get(adapter_type, {})
1586
1846
 
1587
- # Override with command line options if provided
1847
+ # Override with command line options if provided (highest priority)
1588
1848
  if base_path and adapter_type == "aitrackdown":
1589
1849
  adapter_config["base_path"] = base_path
1590
1850
 
@@ -1652,56 +1652,191 @@ async def main():
1652
1652
  # Load configuration
1653
1653
  import json
1654
1654
  import logging
1655
+ import os
1655
1656
  from pathlib import Path
1656
1657
 
1657
1658
  logger = logging.getLogger(__name__)
1658
1659
 
1659
- # ONLY read from project-local config, never from user home
1660
- config_file = Path.cwd() / ".mcp-ticketer" / "config.json"
1661
- if config_file.exists():
1662
- # Validate config is within project
1663
- try:
1664
- if not config_file.resolve().is_relative_to(Path.cwd().resolve()):
1665
- logger.error(
1666
- f"Security violation: Config file {config_file} "
1667
- "is not within project directory"
1668
- )
1669
- raise ValueError(
1670
- f"Security violation: Config file {config_file} "
1671
- "is not within project directory"
1672
- )
1673
- except (ValueError, RuntimeError):
1674
- # is_relative_to may raise ValueError in some cases
1675
- pass
1660
+ # Initialize defaults
1661
+ adapter_type = "aitrackdown"
1662
+ adapter_config = {"base_path": ".aitrackdown"}
1663
+
1664
+ # Priority 1: Check .env files (highest priority for MCP)
1665
+ env_config = _load_env_configuration()
1666
+ if env_config and env_config.get("adapter_type"):
1667
+ adapter_type = env_config["adapter_type"]
1668
+ adapter_config = env_config["adapter_config"]
1669
+ logger.info(f"Using adapter from .env files: {adapter_type}")
1670
+ logger.info(f"Built adapter config from .env: {list(adapter_config.keys())}")
1671
+ else:
1672
+ # Priority 2: Check project-local config file
1673
+ config_file = Path.cwd() / ".mcp-ticketer" / "config.json"
1674
+ if config_file.exists():
1675
+ # Validate config is within project
1676
+ try:
1677
+ if not config_file.resolve().is_relative_to(Path.cwd().resolve()):
1678
+ logger.error(
1679
+ f"Security violation: Config file {config_file} "
1680
+ "is not within project directory"
1681
+ )
1682
+ raise ValueError(
1683
+ f"Security violation: Config file {config_file} "
1684
+ "is not within project directory"
1685
+ )
1686
+ except (ValueError, RuntimeError):
1687
+ # is_relative_to may raise ValueError in some cases
1688
+ pass
1676
1689
 
1677
- try:
1678
- with open(config_file) as f:
1679
- config = json.load(f)
1680
- adapter_type = config.get("default_adapter", "aitrackdown")
1681
- # Get adapter-specific config
1682
- adapters_config = config.get("adapters", {})
1683
- adapter_config = adapters_config.get(adapter_type, {})
1684
- # Fallback to legacy config format
1685
- if not adapter_config and "config" in config:
1686
- adapter_config = config["config"]
1687
- logger.info(
1688
- f"Loaded MCP configuration from project-local: {config_file}"
1689
- )
1690
- except (OSError, json.JSONDecodeError) as e:
1691
- logger.warning(f"Could not load project config: {e}, using defaults")
1690
+ try:
1691
+ with open(config_file) as f:
1692
+ config = json.load(f)
1693
+ adapter_type = config.get("default_adapter", "aitrackdown")
1694
+ # Get adapter-specific config
1695
+ adapters_config = config.get("adapters", {})
1696
+ adapter_config = adapters_config.get(adapter_type, {})
1697
+ # Fallback to legacy config format
1698
+ if not adapter_config and "config" in config:
1699
+ adapter_config = config["config"]
1700
+ logger.info(
1701
+ f"Loaded MCP configuration from project-local: {config_file}"
1702
+ )
1703
+ except (OSError, json.JSONDecodeError) as e:
1704
+ logger.warning(f"Could not load project config: {e}, using defaults")
1705
+ adapter_type = "aitrackdown"
1706
+ adapter_config = {"base_path": ".aitrackdown"}
1707
+ else:
1708
+ # Priority 3: Default to aitrackdown
1709
+ logger.info("No configuration found, defaulting to aitrackdown adapter")
1692
1710
  adapter_type = "aitrackdown"
1693
1711
  adapter_config = {"base_path": ".aitrackdown"}
1694
- else:
1695
- # Default to aitrackdown with local base path
1696
- logger.info("No project-local config found, defaulting to aitrackdown adapter")
1697
- adapter_type = "aitrackdown"
1698
- adapter_config = {"base_path": ".aitrackdown"}
1712
+
1713
+ # Log final configuration for debugging
1714
+ logger.info(f"Starting MCP server with adapter: {adapter_type}")
1715
+ logger.debug(f"Adapter config keys: {list(adapter_config.keys())}")
1699
1716
 
1700
1717
  # Create and run server
1701
1718
  server = MCPTicketServer(adapter_type, adapter_config)
1702
1719
  await server.run()
1703
1720
 
1704
1721
 
1722
+ def _load_env_configuration() -> Optional[dict[str, Any]]:
1723
+ """Load adapter configuration from .env files.
1724
+
1725
+ Checks .env.local first (highest priority), then .env.
1726
+
1727
+ Returns:
1728
+ Dictionary with 'adapter_type' and 'adapter_config' keys, or None if no config found
1729
+ """
1730
+ from pathlib import Path
1731
+
1732
+ # Check for .env files in order of preference
1733
+ env_files = [".env.local", ".env"]
1734
+ env_vars = {}
1735
+
1736
+ for env_file in env_files:
1737
+ env_path = Path.cwd() / env_file
1738
+ if env_path.exists():
1739
+ try:
1740
+ # Parse .env file manually to avoid external dependencies
1741
+ with open(env_path, 'r') as f:
1742
+ for line in f:
1743
+ line = line.strip()
1744
+ if line and not line.startswith('#') and '=' in line:
1745
+ key, value = line.split('=', 1)
1746
+ key = key.strip()
1747
+ value = value.strip().strip('"').strip("'")
1748
+ if value: # Only add non-empty values
1749
+ env_vars[key] = value
1750
+ except Exception:
1751
+ continue
1752
+
1753
+ if not env_vars:
1754
+ return None
1755
+
1756
+ # Determine adapter type and build config
1757
+ adapter_type = env_vars.get("MCP_TICKETER_ADAPTER")
1758
+ if not adapter_type:
1759
+ # Auto-detect based on available keys
1760
+ if any(key.startswith("LINEAR_") for key in env_vars):
1761
+ adapter_type = "linear"
1762
+ elif any(key.startswith("GITHUB_") for key in env_vars):
1763
+ adapter_type = "github"
1764
+ elif any(key.startswith("JIRA_") for key in env_vars):
1765
+ adapter_type = "jira"
1766
+ else:
1767
+ return None
1768
+
1769
+ # Build adapter-specific configuration
1770
+ adapter_config = _build_adapter_config_from_env_vars(adapter_type, env_vars)
1771
+
1772
+ if not adapter_config:
1773
+ return None
1774
+
1775
+ return {
1776
+ "adapter_type": adapter_type,
1777
+ "adapter_config": adapter_config
1778
+ }
1779
+
1780
+
1781
+ def _build_adapter_config_from_env_vars(adapter_type: str, env_vars: dict[str, str]) -> dict[str, Any]:
1782
+ """Build adapter configuration from parsed environment variables.
1783
+
1784
+ Args:
1785
+ adapter_type: Type of adapter to configure
1786
+ env_vars: Dictionary of environment variables from .env files
1787
+
1788
+ Returns:
1789
+ Dictionary of adapter configuration
1790
+ """
1791
+ config = {}
1792
+
1793
+ if adapter_type == "linear":
1794
+ # Linear adapter configuration
1795
+ if env_vars.get("LINEAR_API_KEY"):
1796
+ config["api_key"] = env_vars["LINEAR_API_KEY"]
1797
+ if env_vars.get("LINEAR_TEAM_ID"):
1798
+ config["team_id"] = env_vars["LINEAR_TEAM_ID"]
1799
+ if env_vars.get("LINEAR_TEAM_KEY"):
1800
+ config["team_key"] = env_vars["LINEAR_TEAM_KEY"]
1801
+ if env_vars.get("LINEAR_API_URL"):
1802
+ config["api_url"] = env_vars["LINEAR_API_URL"]
1803
+
1804
+ elif adapter_type == "github":
1805
+ # GitHub adapter configuration
1806
+ if env_vars.get("GITHUB_TOKEN"):
1807
+ config["token"] = env_vars["GITHUB_TOKEN"]
1808
+ if env_vars.get("GITHUB_OWNER"):
1809
+ config["owner"] = env_vars["GITHUB_OWNER"]
1810
+ if env_vars.get("GITHUB_REPO"):
1811
+ config["repo"] = env_vars["GITHUB_REPO"]
1812
+
1813
+ elif adapter_type == "jira":
1814
+ # JIRA adapter configuration
1815
+ if env_vars.get("JIRA_SERVER"):
1816
+ config["server"] = env_vars["JIRA_SERVER"]
1817
+ if env_vars.get("JIRA_EMAIL"):
1818
+ config["email"] = env_vars["JIRA_EMAIL"]
1819
+ if env_vars.get("JIRA_API_TOKEN"):
1820
+ config["api_token"] = env_vars["JIRA_API_TOKEN"]
1821
+ if env_vars.get("JIRA_PROJECT_KEY"):
1822
+ config["project_key"] = env_vars["JIRA_PROJECT_KEY"]
1823
+
1824
+ elif adapter_type == "aitrackdown":
1825
+ # AITrackdown adapter configuration
1826
+ base_path = env_vars.get("MCP_TICKETER_BASE_PATH", ".aitrackdown")
1827
+ config["base_path"] = base_path
1828
+ config["auto_create_dirs"] = True
1829
+
1830
+ # Add any generic overrides
1831
+ if env_vars.get("MCP_TICKETER_API_KEY"):
1832
+ config["api_key"] = env_vars["MCP_TICKETER_API_KEY"]
1833
+
1834
+ return config
1835
+
1836
+
1837
+
1838
+
1839
+
1705
1840
  # Add diagnostic handler methods to MCPTicketServer class
1706
1841
  async def _handle_system_health(self, arguments: dict[str, Any]) -> dict[str, Any]:
1707
1842
  """Handle system health check."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-ticketer
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Universal ticket management interface for AI agents with MCP support
5
5
  Author-email: MCP Ticketer Team <support@mcp-ticketer.io>
6
6
  Maintainer-email: MCP Ticketer Team <support@mcp-ticketer.io>
@@ -1,5 +1,5 @@
1
1
  mcp_ticketer/__init__.py,sha256=Xx4WaprO5PXhVPbYi1L6tBmwmJMkYS-lMyG4ieN6QP0,717
2
- mcp_ticketer/__version__.py,sha256=9j49fzPH9jSgtpm7Tgb5_MiRjdY95EWDMnBbiisOXqM,1117
2
+ mcp_ticketer/__version__.py,sha256=GoFXSWeevjribCSU_VeE2nxxgWFRp71QCl67iMs_z50,1117
3
3
  mcp_ticketer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  mcp_ticketer/adapters/__init__.py,sha256=B5DFllWn23hkhmrLykNO5uMMSdcFuuPHXyLw_jyFzuE,358
5
5
  mcp_ticketer/adapters/aitrackdown.py,sha256=stlbge8K6w-EyQkw_vEQNSXQgCOWN5tOlQUgGWZQNMQ,17936
@@ -16,6 +16,7 @@ mcp_ticketer/adapters/linear/types.py,sha256=VuGPu1Z5jGHtbI2zkCyL5YFnwQNukGW-UV7
16
16
  mcp_ticketer/cache/__init__.py,sha256=Xcd-cKnt-Cx7jBzvfzUUUPaGkmyXFi5XUFWw3Z4b7d4,138
17
17
  mcp_ticketer/cache/memory.py,sha256=2yBqGi9i0SanlUhJoOC7nijWjoMa3_ntPe-V-AV-LfU,5042
18
18
  mcp_ticketer/cli/__init__.py,sha256=l9Q8iKmfGkTu0cssHBVqNZTsL4eAtFzOB25AED_0G6g,89
19
+ mcp_ticketer/cli/adapter_diagnostics.py,sha256=t7RyesBRSyJBoVvft2scdNRcugJDIWvqYS9NszPV5ok,14820
19
20
  mcp_ticketer/cli/auggie_configure.py,sha256=MXKzLtqe3K_UTQ2GacHAWbvf_B0779KL325smiAKE0Q,8212
20
21
  mcp_ticketer/cli/codex_configure.py,sha256=xDppHouT6_-cYXswyAggoPX5bSlRXMvCoM_x9PQ-42A,9086
21
22
  mcp_ticketer/cli/configure.py,sha256=BsA_pSHQMQS0t1bJO_wMM8LWsd5sWJDASjEPRHvwC18,16198
@@ -23,7 +24,7 @@ mcp_ticketer/cli/diagnostics.py,sha256=AC7cMQHVWdHfrYH2Y1tkhmRezM2-mID5E_Dhfil7F
23
24
  mcp_ticketer/cli/discover.py,sha256=AF_qlQc1Oo0UkWayoF5pmRChS5J3fJjH6f2YZzd_k8w,13188
24
25
  mcp_ticketer/cli/gemini_configure.py,sha256=ZNSA1lBW-itVToza-JxW95Po7daVXKiZAh7lp6pmXMU,9343
25
26
  mcp_ticketer/cli/linear_commands.py,sha256=b5v4c9uBNPwj_vdy314JkLZbyC1fXU6IcY2VoG0z7gI,17193
26
- mcp_ticketer/cli/main.py,sha256=9e83BCxds9AESIYueK1VjeJ96H7AvRbvPiMYwd4EeKs,62258
27
+ mcp_ticketer/cli/main.py,sha256=EK2MgphV1Anw_vAtkTl5iC-vzPWIOyu3SV7zOTAY_Vw,72131
27
28
  mcp_ticketer/cli/mcp_configure.py,sha256=RzV50UjXgOmvMp-9S0zS39psuvjffVByaMrqrUaAGAM,9594
28
29
  mcp_ticketer/cli/migrate_config.py,sha256=MYsr_C5ZxsGg0P13etWTWNrJ_lc6ElRCkzfQADYr3DM,5956
29
30
  mcp_ticketer/cli/queue_commands.py,sha256=mm-3H6jmkUGJDyU_E46o9iRpek8tvFCm77F19OtHiZI,7884
@@ -41,7 +42,7 @@ mcp_ticketer/core/models.py,sha256=a_2AbL3NlN0pfdZad-hXus_zb4bSi9zISHcsNYl0sng,1
41
42
  mcp_ticketer/core/project_config.py,sha256=yYxlgxjcEPeOwx-b-SXFpe0k9pW9xzBRAK72PsItG-o,23346
42
43
  mcp_ticketer/core/registry.py,sha256=ShYLDPE62KFJpB0kj_zFyQzRxSH3LkQEEuo1jaakb1k,3483
43
44
  mcp_ticketer/mcp/__init__.py,sha256=Y05eTzsPk0wH8yKNIM-ekpGjgSDO0bQr0EME-vOP4GE,123
44
- mcp_ticketer/mcp/server.py,sha256=vs0WR9_KhDaYyhrkbTuTsEThT0bMj6gwuFmAxmzCxwU,76377
45
+ mcp_ticketer/mcp/server.py,sha256=PCNerYqLMq5et3ZYAIdd_q_-4LGLBhDuvPAYTuWxDiQ,81351
45
46
  mcp_ticketer/queue/__init__.py,sha256=1YIaCpZpFqPcqvDEQXiEvDLiw94DXRdCJkBaVIFQrms,231
46
47
  mcp_ticketer/queue/__main__.py,sha256=gc_tE9NUdK07OJfTZuD4t6KeBD_vxFQIhknGTQUG_jk,109
47
48
  mcp_ticketer/queue/health_monitor.py,sha256=aQrlBzfbLWu8-fV2b5CuHs4oqyTqGGcntKIHM3r-dDI,11844
@@ -50,9 +51,9 @@ mcp_ticketer/queue/queue.py,sha256=jSAkYNEIbNH1cbYuF8s6eFuZmXqn8WHXx3mbfMU2Ud8,1
50
51
  mcp_ticketer/queue/run_worker.py,sha256=F7anuhdkgZF9lXZntHuJ7rEzuEkAfAZO1qvGh3R57bw,1033
51
52
  mcp_ticketer/queue/ticket_registry.py,sha256=k8FYg2cFYsI4POb94-o-fTrIVr-ttfi60r0O5YhJYck,15321
52
53
  mcp_ticketer/queue/worker.py,sha256=zXJpyhRJ99be0VLaez3YPtC9OU17vVNu5qhr1dCGaLg,19992
53
- mcp_ticketer-0.2.0.dist-info/licenses/LICENSE,sha256=KOVrunjtILSzY-2N8Lqa3-Q8dMaZIG4LrlLTr9UqL08,1073
54
- mcp_ticketer-0.2.0.dist-info/METADATA,sha256=bvHYuQNE5HYIEgM2BlrpPwCesfgm3jWiax7lwTNGGB0,13219
55
- mcp_ticketer-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
56
- mcp_ticketer-0.2.0.dist-info/entry_points.txt,sha256=o1IxVhnHnBNG7FZzbFq-Whcs1Djbofs0qMjiUYBLx2s,60
57
- mcp_ticketer-0.2.0.dist-info/top_level.txt,sha256=WnAG4SOT1Vm9tIwl70AbGG_nA217YyV3aWFhxLH2rxw,13
58
- mcp_ticketer-0.2.0.dist-info/RECORD,,
54
+ mcp_ticketer-0.3.0.dist-info/licenses/LICENSE,sha256=KOVrunjtILSzY-2N8Lqa3-Q8dMaZIG4LrlLTr9UqL08,1073
55
+ mcp_ticketer-0.3.0.dist-info/METADATA,sha256=qUgh1KFBMCfrmkev4QBVmCEpbCAbutnjGDv5pTrfyJ8,13219
56
+ mcp_ticketer-0.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
+ mcp_ticketer-0.3.0.dist-info/entry_points.txt,sha256=o1IxVhnHnBNG7FZzbFq-Whcs1Djbofs0qMjiUYBLx2s,60
58
+ mcp_ticketer-0.3.0.dist-info/top_level.txt,sha256=WnAG4SOT1Vm9tIwl70AbGG_nA217YyV3aWFhxLH2rxw,13
59
+ mcp_ticketer-0.3.0.dist-info/RECORD,,