mcp-ticketer 0.3.1__py3-none-any.whl → 0.3.2__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 (37) hide show
  1. mcp_ticketer/__version__.py +1 -1
  2. mcp_ticketer/adapters/aitrackdown.py +12 -15
  3. mcp_ticketer/adapters/github.py +7 -4
  4. mcp_ticketer/adapters/jira.py +23 -22
  5. mcp_ticketer/adapters/linear/__init__.py +1 -1
  6. mcp_ticketer/adapters/linear/adapter.py +88 -89
  7. mcp_ticketer/adapters/linear/client.py +71 -52
  8. mcp_ticketer/adapters/linear/mappers.py +88 -68
  9. mcp_ticketer/adapters/linear/queries.py +28 -7
  10. mcp_ticketer/adapters/linear/types.py +57 -50
  11. mcp_ticketer/adapters/linear.py +2 -2
  12. mcp_ticketer/cli/adapter_diagnostics.py +86 -51
  13. mcp_ticketer/cli/diagnostics.py +165 -72
  14. mcp_ticketer/cli/linear_commands.py +156 -113
  15. mcp_ticketer/cli/main.py +153 -82
  16. mcp_ticketer/cli/simple_health.py +73 -45
  17. mcp_ticketer/cli/utils.py +15 -10
  18. mcp_ticketer/core/config.py +23 -19
  19. mcp_ticketer/core/env_discovery.py +5 -4
  20. mcp_ticketer/core/env_loader.py +109 -86
  21. mcp_ticketer/core/exceptions.py +20 -18
  22. mcp_ticketer/core/models.py +9 -0
  23. mcp_ticketer/core/project_config.py +1 -1
  24. mcp_ticketer/mcp/server.py +294 -139
  25. mcp_ticketer/queue/health_monitor.py +152 -121
  26. mcp_ticketer/queue/manager.py +11 -4
  27. mcp_ticketer/queue/queue.py +15 -3
  28. mcp_ticketer/queue/run_worker.py +1 -1
  29. mcp_ticketer/queue/ticket_registry.py +190 -132
  30. mcp_ticketer/queue/worker.py +54 -25
  31. {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.2.dist-info}/METADATA +1 -1
  32. mcp_ticketer-0.3.2.dist-info/RECORD +59 -0
  33. mcp_ticketer-0.3.1.dist-info/RECORD +0 -59
  34. {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.2.dist-info}/WHEEL +0 -0
  35. {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.2.dist-info}/entry_points.txt +0 -0
  36. {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.2.dist-info}/licenses/LICENSE +0 -0
  37. {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.2.dist-info}/top_level.txt +0 -0
@@ -3,7 +3,7 @@
3
3
  import os
4
4
  import sys
5
5
  from pathlib import Path
6
- from typing import Dict, Any
6
+ from typing import Any, Dict
7
7
 
8
8
  from rich.console import Console
9
9
 
@@ -14,41 +14,45 @@ def simple_health_check() -> int:
14
14
  """Perform a simple health check without heavy dependencies."""
15
15
  console.print("\n🏥 [bold blue]MCP Ticketer Quick Health Check[/bold blue]")
16
16
  console.print("=" * 50)
17
-
17
+
18
18
  issues = 0
19
-
19
+
20
20
  # Check Python version
21
21
  python_version = sys.version_info
22
22
  if python_version >= (3, 9):
23
- console.print(f"✅ Python: {python_version.major}.{python_version.minor}.{python_version.micro}")
23
+ console.print(
24
+ f"✅ Python: {python_version.major}.{python_version.minor}.{python_version.micro}"
25
+ )
24
26
  else:
25
- console.print(f"❌ Python: {python_version.major}.{python_version.minor}.{python_version.micro} (requires 3.9+)")
27
+ console.print(
28
+ f"❌ Python: {python_version.major}.{python_version.minor}.{python_version.micro} (requires 3.9+)"
29
+ )
26
30
  issues += 1
27
-
31
+
28
32
  # Check for basic configuration files
29
33
  config_files = [
30
34
  ".mcp-ticketer.yaml",
31
- ".mcp-ticketer.yml",
35
+ ".mcp-ticketer.yml",
32
36
  "mcp-ticketer.yaml",
33
37
  "mcp-ticketer.yml",
34
38
  ".aitrackdown",
35
39
  ]
36
-
40
+
37
41
  config_found = False
38
42
  for config_file in config_files:
39
43
  if Path(config_file).exists():
40
44
  console.print(f"✅ Configuration: Found {config_file}")
41
45
  config_found = True
42
46
  break
43
-
47
+
44
48
  if not config_found:
45
49
  console.print("⚠️ Configuration: No config files found (will use defaults)")
46
-
50
+
47
51
  # Check for aitrackdown directory (default adapter)
48
52
  aitrackdown_path = Path(".aitrackdown")
49
53
  if aitrackdown_path.exists():
50
54
  console.print(f"✅ Aitrackdown: Directory exists at {aitrackdown_path}")
51
-
55
+
52
56
  # Check for tickets
53
57
  tickets_dir = aitrackdown_path / "tickets"
54
58
  if tickets_dir.exists():
@@ -58,69 +62,81 @@ def simple_health_check() -> int:
58
62
  console.print("ℹ️ Aitrackdown: No tickets directory (will be created)")
59
63
  else:
60
64
  console.print("ℹ️ Aitrackdown: Directory will be created on first use")
61
-
65
+
62
66
  # Check environment variables
63
67
  env_vars = [
64
68
  "LINEAR_API_KEY",
65
- "LINEAR_TEAM_ID",
69
+ "LINEAR_TEAM_ID",
66
70
  "GITHUB_TOKEN",
67
71
  "GITHUB_REPO",
68
72
  "JIRA_SERVER",
69
73
  "JIRA_EMAIL",
70
74
  "JIRA_API_TOKEN",
71
75
  ]
72
-
76
+
73
77
  env_found = []
74
78
  for var in env_vars:
75
79
  if os.getenv(var):
76
80
  env_found.append(var)
77
-
81
+
78
82
  if env_found:
79
83
  console.print(f"✅ Environment: {len(env_found)} adapter variables configured")
80
84
  for var in env_found:
81
85
  console.print(f" • {var}")
82
86
  else:
83
87
  console.print("ℹ️ Environment: No adapter variables found (using defaults)")
84
-
88
+
85
89
  # Check if we can import core modules
86
90
  try:
87
91
  import mcp_ticketer
88
- console.print(f"✅ Installation: mcp-ticketer {mcp_ticketer.__version__} installed")
92
+
93
+ console.print(
94
+ f"✅ Installation: mcp-ticketer {mcp_ticketer.__version__} installed"
95
+ )
89
96
  except Exception as e:
90
97
  console.print(f"❌ Installation: Import failed - {e}")
91
98
  issues += 1
92
-
99
+
93
100
  # Try to check queue system (simplified)
94
101
  try:
95
102
  from ..queue.manager import WorkerManager
103
+
96
104
  worker_manager = WorkerManager()
97
105
  worker_status = worker_manager.get_status()
98
106
 
99
107
  if worker_status.get("running", False):
100
108
  console.print(f"✅ Queue Worker: Running (PID: {worker_status.get('pid')})")
101
109
  else:
102
- console.print("⚠️ Queue Worker: Not running (start with: mcp-ticketer queue worker start)")
110
+ console.print(
111
+ "⚠️ Queue Worker: Not running (start with: mcp-ticketer queue worker start)"
112
+ )
103
113
 
104
114
  # Get basic stats
105
115
  stats = worker_manager.queue.get_stats()
106
116
  total = stats.get("total", 0)
107
117
  failed = stats.get("failed", 0)
108
-
118
+
109
119
  if total > 0:
110
120
  failure_rate = (failed / total) * 100
111
121
  if failure_rate > 50:
112
- console.print(f"❌ Queue Health: High failure rate {failure_rate:.1f}% ({failed}/{total})")
122
+ console.print(
123
+ f"❌ Queue Health: High failure rate {failure_rate:.1f}% ({failed}/{total})"
124
+ )
113
125
  issues += 1
114
126
  elif failure_rate > 20:
115
- console.print(f"⚠️ Queue Health: Elevated failure rate {failure_rate:.1f}% ({failed}/{total})")
127
+ console.print(
128
+ f"⚠️ Queue Health: Elevated failure rate {failure_rate:.1f}% ({failed}/{total})"
129
+ )
116
130
  else:
117
- console.print(f"✅ Queue Health: {failure_rate:.1f}% failure rate ({failed}/{total})")
131
+ console.print(
132
+ f"✅ Queue Health: {failure_rate:.1f}% failure rate ({failed}/{total})"
133
+ )
118
134
  else:
119
135
  console.print("ℹ️ Queue Health: No items processed yet")
120
-
136
+
121
137
  except Exception as e:
122
138
  console.print(f"⚠️ Queue System: Could not check status - {e}")
123
-
139
+
124
140
  # Summary
125
141
  console.print()
126
142
  if issues == 0:
@@ -137,7 +153,7 @@ def simple_diagnose() -> Dict[str, Any]:
137
153
  """Simple diagnosis that works without full config system."""
138
154
  console.print("\n🔍 [bold blue]MCP Ticketer Simple Diagnosis[/bold blue]")
139
155
  console.print("=" * 60)
140
-
156
+
141
157
  report = {
142
158
  "timestamp": "2025-10-24", # Static for now
143
159
  "version": "0.1.28",
@@ -147,73 +163,85 @@ def simple_diagnose() -> Dict[str, Any]:
147
163
  "warnings": [],
148
164
  "recommendations": [],
149
165
  }
150
-
166
+
151
167
  # Basic checks
152
168
  console.print("\n📋 [yellow]Basic System Check[/yellow]")
153
-
169
+
154
170
  # Python version
155
171
  if sys.version_info < (3, 9):
156
172
  issue = f"Python {sys.version_info.major}.{sys.version_info.minor} is too old (requires 3.9+)"
157
173
  report["issues"].append(issue)
158
174
  console.print(f"❌ {issue}")
159
175
  else:
160
- console.print(f"✅ Python {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}")
161
-
176
+ console.print(
177
+ f"✅ Python {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
178
+ )
179
+
162
180
  # Installation check
163
181
  try:
164
182
  import mcp_ticketer
183
+
165
184
  console.print(f"✅ mcp-ticketer {mcp_ticketer.__version__} installed")
166
185
  except Exception as e:
167
186
  issue = f"Installation check failed: {e}"
168
187
  report["issues"].append(issue)
169
188
  console.print(f"❌ {issue}")
170
-
189
+
171
190
  # Configuration check
172
191
  console.print("\n📋 [yellow]Configuration Check[/yellow]")
173
- config_files = [".mcp-ticketer.yaml", ".mcp-ticketer.yml", "mcp-ticketer.yaml", "mcp-ticketer.yml"]
192
+ config_files = [
193
+ ".mcp-ticketer.yaml",
194
+ ".mcp-ticketer.yml",
195
+ "mcp-ticketer.yaml",
196
+ "mcp-ticketer.yml",
197
+ ]
174
198
  config_found = any(Path(f).exists() for f in config_files)
175
-
199
+
176
200
  if config_found:
177
201
  console.print("✅ Configuration files found")
178
202
  else:
179
203
  console.print("ℹ️ No configuration files (using defaults)")
180
-
204
+
181
205
  # Environment variables
182
206
  env_vars = ["LINEAR_API_KEY", "GITHUB_TOKEN", "JIRA_SERVER"]
183
207
  env_count = sum(1 for var in env_vars if os.getenv(var))
184
-
208
+
185
209
  if env_count > 0:
186
210
  console.print(f"✅ {env_count} adapter environment variables configured")
187
211
  else:
188
212
  console.print("ℹ️ No adapter environment variables (using aitrackdown)")
189
-
213
+
190
214
  # Recommendations
191
215
  if not report["issues"]:
192
216
  report["recommendations"].append("✅ System appears healthy")
193
217
  else:
194
218
  report["recommendations"].append("🚨 Critical issues detected - see above")
195
-
219
+
196
220
  if not config_found and env_count == 0:
197
- report["recommendations"].append("💡 Consider running: mcp-ticketer init-aitrackdown")
198
-
221
+ report["recommendations"].append(
222
+ "💡 Consider running: mcp-ticketer init-aitrackdown"
223
+ )
224
+
199
225
  # Display summary
200
226
  console.print("\n" + "=" * 60)
201
227
  console.print("📋 [bold green]DIAGNOSIS SUMMARY[/bold green]")
202
228
  console.print("=" * 60)
203
-
229
+
204
230
  if report["issues"]:
205
231
  console.print(f"\n🚨 [bold red]Issues ({len(report['issues'])}):[/bold red]")
206
232
  for issue in report["issues"]:
207
233
  console.print(f" • {issue}")
208
-
234
+
209
235
  if report["warnings"]:
210
- console.print(f"\n⚠️ [bold yellow]Warnings ({len(report['warnings'])}):[/bold yellow]")
236
+ console.print(
237
+ f"\n⚠️ [bold yellow]Warnings ({len(report['warnings'])}):[/bold yellow]"
238
+ )
211
239
  for warning in report["warnings"]:
212
240
  console.print(f" • {warning}")
213
-
241
+
214
242
  if report["recommendations"]:
215
- console.print(f"\n💡 [bold blue]Recommendations:[/bold blue]")
243
+ console.print("\n💡 [bold blue]Recommendations:[/bold blue]")
216
244
  for rec in report["recommendations"]:
217
245
  console.print(f" {rec}")
218
-
246
+
219
247
  return report
mcp_ticketer/cli/utils.py CHANGED
@@ -81,6 +81,7 @@ class CommonPatterns:
81
81
  # Try environment discovery as fallback
82
82
  try:
83
83
  from ..core.config import ConfigurationManager
84
+
84
85
  config_manager = ConfigurationManager()
85
86
  app_config = config_manager.load_config()
86
87
 
@@ -88,24 +89,27 @@ class CommonPatterns:
88
89
  enabled_adapters = app_config.get_enabled_adapters()
89
90
  if enabled_adapters:
90
91
  # Use the first enabled adapter as default
91
- default_adapter = app_config.default_adapter or list(enabled_adapters.keys())[0]
92
+ default_adapter = (
93
+ app_config.default_adapter or list(enabled_adapters.keys())[0]
94
+ )
92
95
 
93
96
  # Convert to legacy format
94
- legacy_config = {
95
- "default_adapter": default_adapter,
96
- "adapters": {}
97
- }
97
+ legacy_config = {"default_adapter": default_adapter, "adapters": {}}
98
98
 
99
99
  # Convert adapter configs to dict format
100
100
  for name, adapter_config in enabled_adapters.items():
101
- if hasattr(adapter_config, 'model_dump'):
102
- legacy_config["adapters"][name] = adapter_config.model_dump(exclude_none=False)
103
- elif hasattr(adapter_config, 'dict'):
101
+ if hasattr(adapter_config, "model_dump"):
102
+ legacy_config["adapters"][name] = adapter_config.model_dump(
103
+ exclude_none=False
104
+ )
105
+ elif hasattr(adapter_config, "dict"):
104
106
  legacy_config["adapters"][name] = adapter_config.dict()
105
107
  else:
106
108
  legacy_config["adapters"][name] = adapter_config
107
109
 
108
- logger.info(f"Loaded configuration from environment discovery: {list(enabled_adapters.keys())}")
110
+ logger.info(
111
+ f"Loaded configuration from environment discovery: {list(enabled_adapters.keys())}"
112
+ )
109
113
  return legacy_config
110
114
 
111
115
  except Exception as e:
@@ -212,12 +216,13 @@ class CommonPatterns:
212
216
 
213
217
  # Add to queue with explicit project directory
214
218
  from pathlib import Path
219
+
215
220
  queue = Queue()
216
221
  queue_id = queue.add(
217
222
  ticket_data=ticket_data,
218
223
  adapter=adapter_name,
219
224
  operation=operation,
220
- project_dir=str(Path.cwd()) # Explicitly pass current project directory
225
+ project_dir=str(Path.cwd()), # Explicitly pass current project directory
221
226
  )
222
227
 
223
228
  if show_progress:
@@ -119,7 +119,7 @@ class LinearConfig(BaseAdapterConfig):
119
119
  api_key: Optional[str] = Field(None, env="LINEAR_API_KEY")
120
120
  workspace: Optional[str] = None
121
121
  team_key: Optional[str] = None # Short team key like "BTA"
122
- team_id: Optional[str] = None # UUID team identifier
122
+ team_id: Optional[str] = None # UUID team identifier
123
123
  api_url: str = "https://api.linear.app/graphql"
124
124
 
125
125
  @model_validator(mode="after")
@@ -366,30 +366,26 @@ class ConfigurationManager:
366
366
  "aitrackdown": {
367
367
  "type": "aitrackdown",
368
368
  "enabled": True,
369
- "base_path": str(Path.home() / ".mcp-ticketer" / ".aitrackdown")
369
+ "base_path": str(
370
+ Path.home() / ".mcp-ticketer" / ".aitrackdown"
371
+ ),
370
372
  }
371
373
  },
372
- "default_adapter": "aitrackdown"
374
+ "default_adapter": "aitrackdown",
373
375
  }
374
376
 
375
377
  # Convert discovered adapters to config format
376
- config_data = {
377
- "adapters": {},
378
- "default_adapter": None
379
- }
378
+ config_data = {"adapters": {}, "default_adapter": None}
380
379
 
381
380
  for adapter in discovered.adapters:
382
- adapter_config = {
383
- "type": adapter.adapter_type,
384
- "enabled": True
385
- }
381
+ adapter_config = {"type": adapter.adapter_type, "enabled": True}
386
382
 
387
383
  # Add adapter-specific configuration from discovered config
388
384
  adapter_config.update(adapter.config)
389
385
 
390
386
  # Ensure type is set correctly (remove 'adapter' key if present)
391
- if 'adapter' in adapter_config:
392
- del adapter_config['adapter']
387
+ if "adapter" in adapter_config:
388
+ del adapter_config["adapter"]
393
389
 
394
390
  # Use adapter type as the key name
395
391
  adapter_name = adapter.adapter_type
@@ -399,20 +395,26 @@ class ConfigurationManager:
399
395
  if config_data["default_adapter"] is None:
400
396
  config_data["default_adapter"] = adapter.adapter_type
401
397
 
402
- logger.info(f"Discovered {len(config_data['adapters'])} adapter(s) from environment")
398
+ logger.info(
399
+ f"Discovered {len(config_data['adapters'])} adapter(s) from environment"
400
+ )
403
401
  return config_data
404
402
 
405
403
  except ImportError:
406
- logger.warning("Environment discovery not available, using aitrackdown fallback")
404
+ logger.warning(
405
+ "Environment discovery not available, using aitrackdown fallback"
406
+ )
407
407
  return {
408
408
  "adapters": {
409
409
  "aitrackdown": {
410
410
  "type": "aitrackdown",
411
411
  "enabled": True,
412
- "base_path": str(Path.home() / ".mcp-ticketer" / ".aitrackdown")
412
+ "base_path": str(
413
+ Path.home() / ".mcp-ticketer" / ".aitrackdown"
414
+ ),
413
415
  }
414
416
  },
415
- "default_adapter": "aitrackdown"
417
+ "default_adapter": "aitrackdown",
416
418
  }
417
419
  except Exception as e:
418
420
  logger.error(f"Environment discovery failed: {e}")
@@ -421,10 +423,12 @@ class ConfigurationManager:
421
423
  "aitrackdown": {
422
424
  "type": "aitrackdown",
423
425
  "enabled": True,
424
- "base_path": str(Path.home() / ".mcp-ticketer" / ".aitrackdown")
426
+ "base_path": str(
427
+ Path.home() / ".mcp-ticketer" / ".aitrackdown"
428
+ ),
425
429
  }
426
430
  },
427
- "default_adapter": "aitrackdown"
431
+ "default_adapter": "aitrackdown",
428
432
  }
429
433
 
430
434
  def get_config(self) -> AppConfig:
@@ -229,6 +229,7 @@ class EnvDiscovery:
229
229
 
230
230
  # First, load from actual environment variables (lowest priority)
231
231
  import os
232
+
232
233
  actual_env = {k: v for k, v in os.environ.items() if v}
233
234
  merged_env.update(actual_env)
234
235
  if actual_env:
@@ -300,7 +301,7 @@ class EnvDiscovery:
300
301
  team_identifier = self._find_key_value(env_vars, LINEAR_TEAM_PATTERNS)
301
302
  if team_identifier:
302
303
  # Determine if it's a team_id (UUID format) or team_key (short string)
303
- if len(team_identifier) > 20 and '-' in team_identifier:
304
+ if len(team_identifier) > 20 and "-" in team_identifier:
304
305
  # Looks like a UUID (team_id)
305
306
  config["team_id"] = team_identifier
306
307
  else:
@@ -467,9 +468,9 @@ class EnvDiscovery:
467
468
  # 1. There's an explicit base_path setting, OR
468
469
  # 2. There's a .aitrackdown directory AND no other adapter variables are present
469
470
  has_other_adapter_vars = (
470
- any(key.startswith("LINEAR_") for key in env_vars) or
471
- any(key.startswith("GITHUB_") for key in env_vars) or
472
- any(key.startswith("JIRA_") for key in env_vars)
471
+ any(key.startswith("LINEAR_") for key in env_vars)
472
+ or any(key.startswith("GITHUB_") for key in env_vars)
473
+ or any(key.startswith("JIRA_") for key in env_vars)
473
474
  )
474
475
 
475
476
  if not base_path and not aitrackdown_dir.exists():