mcp-ticketer 0.3.2__py3-none-any.whl → 0.3.4__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 (31) hide show
  1. mcp_ticketer/__version__.py +1 -1
  2. mcp_ticketer/adapters/aitrackdown.py +152 -21
  3. mcp_ticketer/adapters/github.py +4 -4
  4. mcp_ticketer/adapters/jira.py +6 -6
  5. mcp_ticketer/adapters/linear/adapter.py +121 -17
  6. mcp_ticketer/adapters/linear/client.py +7 -7
  7. mcp_ticketer/adapters/linear/mappers.py +9 -10
  8. mcp_ticketer/adapters/linear/types.py +10 -10
  9. mcp_ticketer/cli/adapter_diagnostics.py +2 -2
  10. mcp_ticketer/cli/codex_configure.py +6 -6
  11. mcp_ticketer/cli/diagnostics.py +17 -18
  12. mcp_ticketer/cli/main.py +15 -0
  13. mcp_ticketer/cli/simple_health.py +5 -10
  14. mcp_ticketer/core/env_loader.py +13 -13
  15. mcp_ticketer/core/exceptions.py +5 -5
  16. mcp_ticketer/core/models.py +30 -2
  17. mcp_ticketer/mcp/constants.py +58 -0
  18. mcp_ticketer/mcp/dto.py +195 -0
  19. mcp_ticketer/mcp/response_builder.py +206 -0
  20. mcp_ticketer/mcp/server.py +311 -1287
  21. mcp_ticketer/queue/health_monitor.py +14 -14
  22. mcp_ticketer/queue/manager.py +59 -15
  23. mcp_ticketer/queue/queue.py +9 -2
  24. mcp_ticketer/queue/ticket_registry.py +15 -15
  25. mcp_ticketer/queue/worker.py +25 -18
  26. {mcp_ticketer-0.3.2.dist-info → mcp_ticketer-0.3.4.dist-info}/METADATA +1 -1
  27. {mcp_ticketer-0.3.2.dist-info → mcp_ticketer-0.3.4.dist-info}/RECORD +31 -28
  28. {mcp_ticketer-0.3.2.dist-info → mcp_ticketer-0.3.4.dist-info}/WHEEL +0 -0
  29. {mcp_ticketer-0.3.2.dist-info → mcp_ticketer-0.3.4.dist-info}/entry_points.txt +0 -0
  30. {mcp_ticketer-0.3.2.dist-info → mcp_ticketer-0.3.4.dist-info}/licenses/LICENSE +0 -0
  31. {mcp_ticketer-0.3.2.dist-info → mcp_ticketer-0.3.4.dist-info}/top_level.txt +0 -0
@@ -4,7 +4,7 @@ import logging
4
4
  import time
5
5
  from datetime import datetime, timedelta
6
6
  from enum import Enum
7
- from typing import Any, Dict, List, Optional
7
+ from typing import Any, Optional
8
8
 
9
9
  import psutil
10
10
 
@@ -30,7 +30,7 @@ class HealthAlert:
30
30
  self,
31
31
  level: HealthStatus,
32
32
  message: str,
33
- details: Optional[Dict[str, Any]] = None,
33
+ details: Optional[dict[str, Any]] = None,
34
34
  timestamp: Optional[datetime] = None,
35
35
  ):
36
36
  self.level = level
@@ -62,9 +62,9 @@ class QueueHealthMonitor:
62
62
  self.queue = queue or Queue()
63
63
  self.manager = WorkerManager()
64
64
  self.last_check = datetime.now()
65
- self.alerts: List[HealthAlert] = []
65
+ self.alerts: list[HealthAlert] = []
66
66
 
67
- def check_health(self) -> Dict[str, Any]:
67
+ def check_health(self) -> dict[str, Any]:
68
68
  """Perform comprehensive health check.
69
69
 
70
70
  Returns:
@@ -110,7 +110,7 @@ class QueueHealthMonitor:
110
110
  self.last_check = datetime.now()
111
111
  return health_report
112
112
 
113
- def _check_worker_health(self) -> Dict[str, Any]:
113
+ def _check_worker_health(self) -> dict[str, Any]:
114
114
  """Check worker process health."""
115
115
  worker_status = self.manager.get_status()
116
116
 
@@ -145,7 +145,7 @@ class QueueHealthMonitor:
145
145
  pid = worker_status.get("pid")
146
146
  if pid:
147
147
  try:
148
- process = psutil.Process(pid)
148
+ psutil.Process(pid)
149
149
  # Check if worker has been idle too long with pending items
150
150
  pending_count = self.queue.get_pending_count()
151
151
  if pending_count > 0:
@@ -173,7 +173,7 @@ class QueueHealthMonitor:
173
173
 
174
174
  return metrics
175
175
 
176
- def _check_queue_health(self) -> Dict[str, Any]:
176
+ def _check_queue_health(self) -> dict[str, Any]:
177
177
  """Check queue status and backlog."""
178
178
  stats = self.queue.get_stats()
179
179
 
@@ -220,7 +220,7 @@ class QueueHealthMonitor:
220
220
 
221
221
  return metrics
222
222
 
223
- def _check_stuck_items(self) -> Dict[str, Any]:
223
+ def _check_stuck_items(self) -> dict[str, Any]:
224
224
  """Check for items stuck in processing state."""
225
225
  # Reset stuck items first
226
226
  self.queue.reset_stuck_items(timeout_minutes=5) # 5 minute timeout
@@ -247,7 +247,7 @@ class QueueHealthMonitor:
247
247
 
248
248
  return metrics
249
249
 
250
- def _check_failure_rates(self) -> Dict[str, Any]:
250
+ def _check_failure_rates(self) -> dict[str, Any]:
251
251
  """Check recent failure rates."""
252
252
  stats = self.queue.get_stats()
253
253
 
@@ -288,21 +288,21 @@ class QueueHealthMonitor:
288
288
 
289
289
  return HealthStatus.HEALTHY
290
290
 
291
- def _get_old_pending_items(self) -> List:
291
+ def _get_old_pending_items(self) -> list:
292
292
  """Get items that have been pending for too long."""
293
293
  cutoff_time = datetime.now() - timedelta(seconds=self.WORKER_TIMEOUT_SECONDS)
294
294
 
295
295
  items = self.queue.list_items(status=QueueStatus.PENDING, limit=100)
296
296
  return [item for item in items if item.created_at < cutoff_time]
297
297
 
298
- def _get_stuck_processing_items(self) -> List:
298
+ def _get_stuck_processing_items(self) -> list:
299
299
  """Get items stuck in processing state."""
300
300
  cutoff_time = datetime.now() - timedelta(seconds=self.STUCK_ITEM_THRESHOLD)
301
301
 
302
302
  items = self.queue.list_items(status=QueueStatus.PROCESSING, limit=100)
303
303
  return [item for item in items if item.created_at < cutoff_time]
304
304
 
305
- def get_immediate_alerts(self) -> List[HealthAlert]:
305
+ def get_immediate_alerts(self) -> list[HealthAlert]:
306
306
  """Get alerts that require immediate attention."""
307
307
  return [
308
308
  alert
@@ -310,12 +310,12 @@ class QueueHealthMonitor:
310
310
  if alert.level in [HealthStatus.CRITICAL, HealthStatus.FAILED]
311
311
  ]
312
312
 
313
- def auto_repair(self) -> Dict[str, Any]:
313
+ def auto_repair(self) -> dict[str, Any]:
314
314
  """Attempt automatic repair of detected issues."""
315
315
  repair_actions = []
316
316
 
317
317
  # Check health first
318
- health = self.check_health()
318
+ self.check_health()
319
319
 
320
320
  for alert in self.alerts:
321
321
  action = alert.details.get("action")
@@ -81,12 +81,22 @@ class WorkerManager:
81
81
  # Try to start worker
82
82
  return self.start()
83
83
 
84
- def start(self) -> bool:
84
+ def start(self, timeout: float = 30.0) -> bool:
85
85
  """Start the worker process.
86
86
 
87
+ Args:
88
+ timeout: Maximum seconds to wait for worker to become fully operational (default: 30)
89
+
87
90
  Returns:
88
91
  True if started successfully, False otherwise
89
92
 
93
+ Raises:
94
+ TimeoutError: If worker doesn't start within timeout period
95
+
96
+ Note:
97
+ If timeout occurs, a worker may already be running in another process.
98
+ Check for stale lock files or processes using `get_status()`.
99
+
90
100
  """
91
101
  # Check if already running
92
102
  if self.is_running():
@@ -96,7 +106,19 @@ class WorkerManager:
96
106
  # Try to acquire lock
97
107
  if not self._acquire_lock():
98
108
  logger.warning("Could not acquire lock - another worker may be running")
99
- return False
109
+ # Wait briefly to see if worker becomes operational
110
+ start_time = time.time()
111
+ while time.time() - start_time < timeout:
112
+ if self.is_running():
113
+ logger.info("Worker started by another process")
114
+ return True
115
+ time.sleep(1)
116
+
117
+ raise TimeoutError(
118
+ f"Worker start timed out after {timeout}s. "
119
+ f"Lock is held but worker is not running. "
120
+ f"Check for stale lock files or zombie processes."
121
+ )
100
122
 
101
123
  try:
102
124
  # Start worker in subprocess using the same Python executable as the CLI
@@ -133,20 +155,42 @@ class WorkerManager:
133
155
  # Save PID
134
156
  self.pid_file.write_text(str(process.pid))
135
157
 
136
- # Give the process a moment to start
137
- import time
138
-
139
- time.sleep(0.5)
140
-
141
- # Verify process is running
142
- if not psutil.pid_exists(process.pid):
143
- logger.error("Worker process died immediately after starting")
144
- self._cleanup()
145
- return False
146
-
147
- logger.info(f"Started worker process with PID {process.pid}")
148
- return True
158
+ # Wait for worker to become fully operational with timeout
159
+ start_time = time.time()
160
+ while time.time() - start_time < timeout:
161
+ # Check if process exists
162
+ if not psutil.pid_exists(process.pid):
163
+ logger.error("Worker process died during startup")
164
+ self._cleanup()
165
+ return False
166
+
167
+ # Check if worker is fully running (not just process exists)
168
+ if self.is_running():
169
+ elapsed = time.time() - start_time
170
+ logger.info(
171
+ f"Worker started successfully in {elapsed:.1f}s with PID {process.pid}"
172
+ )
173
+ return True
174
+
175
+ # Log progress for long startups
176
+ elapsed = time.time() - start_time
177
+ if elapsed > 5 and int(elapsed) % 5 == 0:
178
+ logger.debug(
179
+ f"Waiting for worker to start, elapsed: {elapsed:.1f}s"
180
+ )
181
+
182
+ time.sleep(0.5)
183
+
184
+ # Timeout reached
185
+ raise TimeoutError(
186
+ f"Worker start timed out after {timeout}s. "
187
+ f"Process spawned (PID {process.pid}) but worker did not become operational. "
188
+ f"Check worker logs for startup errors."
189
+ )
149
190
 
191
+ except TimeoutError:
192
+ # Re-raise timeout errors with context preserved
193
+ raise
150
194
  except Exception as e:
151
195
  logger.error(f"Failed to start worker: {e}")
152
196
  self._release_lock()
@@ -35,6 +35,7 @@ class QueueItem:
35
35
  retry_count: int = 0
36
36
  result: Optional[dict[str, Any]] = None
37
37
  project_dir: Optional[str] = None
38
+ adapter_config: Optional[dict[str, Any]] = None # Adapter configuration
38
39
 
39
40
  def to_dict(self) -> dict:
40
41
  """Convert to dictionary for storage."""
@@ -59,6 +60,7 @@ class QueueItem:
59
60
  retry_count=row[8],
60
61
  result=json.loads(row[9]) if row[9] else None,
61
62
  project_dir=row[10] if len(row) > 10 else None,
63
+ adapter_config=json.loads(row[11]) if len(row) > 11 and row[11] else None,
62
64
  )
63
65
 
64
66
 
@@ -127,6 +129,8 @@ class Queue:
127
129
  columns = [row[1] for row in cursor.fetchall()]
128
130
  if "project_dir" not in columns:
129
131
  conn.execute("ALTER TABLE queue ADD COLUMN project_dir TEXT")
132
+ if "adapter_config" not in columns:
133
+ conn.execute("ALTER TABLE queue ADD COLUMN adapter_config TEXT")
130
134
 
131
135
  conn.commit()
132
136
 
@@ -136,6 +140,7 @@ class Queue:
136
140
  adapter: str,
137
141
  operation: str,
138
142
  project_dir: Optional[str] = None,
143
+ adapter_config: Optional[dict[str, Any]] = None,
139
144
  ) -> str:
140
145
  """Add item to queue.
141
146
 
@@ -144,6 +149,7 @@ class Queue:
144
149
  adapter: Name of the adapter to use
145
150
  operation: Operation to perform (create, update, delete, etc.)
146
151
  project_dir: Project directory for config resolution (defaults to current directory)
152
+ adapter_config: Adapter configuration to use (optional, for explicit config passing)
147
153
 
148
154
  Returns:
149
155
  Queue ID for tracking
@@ -161,8 +167,8 @@ class Queue:
161
167
  """
162
168
  INSERT INTO queue (
163
169
  id, ticket_data, adapter, operation,
164
- status, created_at, retry_count, project_dir
165
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
170
+ status, created_at, retry_count, project_dir, adapter_config
171
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
166
172
  """,
167
173
  (
168
174
  queue_id,
@@ -173,6 +179,7 @@ class Queue:
173
179
  datetime.now().isoformat(),
174
180
  0,
175
181
  project_dir,
182
+ json.dumps(adapter_config) if adapter_config else None,
176
183
  ),
177
184
  )
178
185
  conn.commit()
@@ -5,7 +5,7 @@ import sqlite3
5
5
  import threading
6
6
  from datetime import datetime, timedelta
7
7
  from pathlib import Path
8
- from typing import Any, Dict, List, Optional
8
+ from typing import Any, Optional
9
9
 
10
10
 
11
11
  class TicketRegistry:
@@ -91,7 +91,7 @@ class TicketRegistry:
91
91
  adapter: str,
92
92
  operation: str,
93
93
  title: str,
94
- ticket_data: Dict[str, Any],
94
+ ticket_data: dict[str, Any],
95
95
  ) -> None:
96
96
  """Register a new ticket operation.
97
97
 
@@ -131,7 +131,7 @@ class TicketRegistry:
131
131
  queue_id: str,
132
132
  status: str,
133
133
  ticket_id: Optional[str] = None,
134
- result_data: Optional[Dict[str, Any]] = None,
134
+ result_data: Optional[dict[str, Any]] = None,
135
135
  error_message: Optional[str] = None,
136
136
  retry_count: Optional[int] = None,
137
137
  ) -> None:
@@ -179,7 +179,7 @@ class TicketRegistry:
179
179
  )
180
180
  conn.commit()
181
181
 
182
- def get_ticket_info(self, queue_id: str) -> Optional[Dict[str, Any]]:
182
+ def get_ticket_info(self, queue_id: str) -> Optional[dict[str, Any]]:
183
183
  """Get ticket information by queue ID.
184
184
 
185
185
  Args:
@@ -212,7 +212,7 @@ class TicketRegistry:
212
212
 
213
213
  return ticket_info
214
214
 
215
- def find_tickets_by_id(self, ticket_id: str) -> List[Dict[str, Any]]:
215
+ def find_tickets_by_id(self, ticket_id: str) -> list[dict[str, Any]]:
216
216
  """Find all operations for a specific ticket ID.
217
217
 
218
218
  Args:
@@ -225,7 +225,7 @@ class TicketRegistry:
225
225
  with sqlite3.connect(self.db_path) as conn:
226
226
  cursor = conn.execute(
227
227
  """
228
- SELECT * FROM ticket_registry
228
+ SELECT * FROM ticket_registry
229
229
  WHERE ticket_id = ?
230
230
  ORDER BY created_at DESC
231
231
  """,
@@ -248,7 +248,7 @@ class TicketRegistry:
248
248
 
249
249
  return results
250
250
 
251
- def get_failed_operations(self, limit: int = 50) -> List[Dict[str, Any]]:
251
+ def get_failed_operations(self, limit: int = 50) -> list[dict[str, Any]]:
252
252
  """Get failed operations that might need recovery.
253
253
 
254
254
  Args:
@@ -261,7 +261,7 @@ class TicketRegistry:
261
261
  with sqlite3.connect(self.db_path) as conn:
262
262
  cursor = conn.execute(
263
263
  """
264
- SELECT * FROM ticket_registry
264
+ SELECT * FROM ticket_registry
265
265
  WHERE status = 'failed'
266
266
  ORDER BY updated_at DESC
267
267
  LIMIT ?
@@ -285,7 +285,7 @@ class TicketRegistry:
285
285
 
286
286
  return results
287
287
 
288
- def get_orphaned_tickets(self) -> List[Dict[str, Any]]:
288
+ def get_orphaned_tickets(self) -> list[dict[str, Any]]:
289
289
  """Get tickets that were created but queue operation failed.
290
290
 
291
291
  Returns:
@@ -295,8 +295,8 @@ class TicketRegistry:
295
295
  with sqlite3.connect(self.db_path) as conn:
296
296
  cursor = conn.execute(
297
297
  """
298
- SELECT * FROM ticket_registry
299
- WHERE ticket_id IS NOT NULL
298
+ SELECT * FROM ticket_registry
299
+ WHERE ticket_id IS NOT NULL
300
300
  AND status IN ('processing', 'failed')
301
301
  ORDER BY updated_at DESC
302
302
  """
@@ -318,7 +318,7 @@ class TicketRegistry:
318
318
 
319
319
  return results
320
320
 
321
- def attempt_recovery(self, queue_id: str, recovery_type: str) -> Dict[str, Any]:
321
+ def attempt_recovery(self, queue_id: str, recovery_type: str) -> dict[str, Any]:
322
322
  """Attempt to recover a failed operation.
323
323
 
324
324
  Args:
@@ -390,7 +390,7 @@ class TicketRegistry:
390
390
  self,
391
391
  queue_id: str,
392
392
  recovery_type: str,
393
- recovery_data: Dict[str, Any],
393
+ recovery_data: dict[str, Any],
394
394
  success: bool,
395
395
  ) -> None:
396
396
  """Log recovery attempt."""
@@ -412,7 +412,7 @@ class TicketRegistry:
412
412
  )
413
413
  conn.commit()
414
414
 
415
- def get_recovery_history(self, queue_id: str) -> List[Dict[str, Any]]:
415
+ def get_recovery_history(self, queue_id: str) -> list[dict[str, Any]]:
416
416
  """Get recovery history for a queue operation.
417
417
 
418
418
  Args:
@@ -425,7 +425,7 @@ class TicketRegistry:
425
425
  with sqlite3.connect(self.db_path) as conn:
426
426
  cursor = conn.execute(
427
427
  """
428
- SELECT * FROM recovery_log
428
+ SELECT * FROM recovery_log
429
429
  WHERE queue_id = ?
430
430
  ORDER BY timestamp DESC
431
431
  """,
@@ -373,24 +373,31 @@ class Worker:
373
373
 
374
374
  from ..cli.main import load_config
375
375
 
376
- # Use item's project_dir if available, otherwise use current directory
377
- project_path = Path(item.project_dir) if item.project_dir else None
378
-
379
- # Load environment variables from project directory's .env.local if it exists
380
- if project_path:
381
- env_file = project_path / ".env.local"
382
- if env_file.exists():
383
- logger.info(f"Worker loading environment from {env_file}")
384
- load_dotenv(env_file)
385
-
386
- logger.info(f"Worker project_path: {project_path}")
387
- logger.info(f"Worker current working directory: {os.getcwd()}")
388
-
389
- config = load_config(project_dir=project_path)
390
- logger.info(f"Worker loaded config: {config}")
391
- adapters_config = config.get("adapters", {})
392
- adapter_config = adapters_config.get(item.adapter, {})
393
- logger.info(f"Worker adapter config for {item.adapter}: {adapter_config}")
376
+ # PRIORITY 1: Use adapter_config from queue item if available (explicit config)
377
+ if item.adapter_config:
378
+ logger.info("Worker using explicit adapter_config from queue item")
379
+ adapter_config = item.adapter_config
380
+ logger.info(f"Worker adapter config for {item.adapter}: {adapter_config}")
381
+ else:
382
+ # PRIORITY 2: Load from project config file
383
+ # Use item's project_dir if available, otherwise use current directory
384
+ project_path = Path(item.project_dir) if item.project_dir else None
385
+
386
+ # Load environment variables from project directory's .env.local if it exists
387
+ if project_path:
388
+ env_file = project_path / ".env.local"
389
+ if env_file.exists():
390
+ logger.info(f"Worker loading environment from {env_file}")
391
+ load_dotenv(env_file)
392
+
393
+ logger.info(f"Worker project_path: {project_path}")
394
+ logger.info(f"Worker current working directory: {os.getcwd()}")
395
+
396
+ config = load_config(project_dir=project_path)
397
+ logger.info(f"Worker loaded config: {config}")
398
+ adapters_config = config.get("adapters", {})
399
+ adapter_config = adapters_config.get(item.adapter, {})
400
+ logger.info(f"Worker adapter config for {item.adapter}: {adapter_config}")
394
401
 
395
402
  # Add environment variables for authentication
396
403
  if item.adapter == "linear":
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-ticketer
3
- Version: 0.3.2
3
+ Version: 0.3.4
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,59 +1,62 @@
1
1
  mcp_ticketer/__init__.py,sha256=Xx4WaprO5PXhVPbYi1L6tBmwmJMkYS-lMyG4ieN6QP0,717
2
- mcp_ticketer/__version__.py,sha256=Gji_cXp3J3NBXvkGk3rFpPcnLRZSQSEud52OdFOkP5c,1117
2
+ mcp_ticketer/__version__.py,sha256=XrTye_cA_opF1wX2csBbZnPCpEQ57tGLh94P_i6prws,1117
3
3
  mcp_ticketer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  mcp_ticketer/adapters/__init__.py,sha256=B5DFllWn23hkhmrLykNO5uMMSdcFuuPHXyLw_jyFzuE,358
5
- mcp_ticketer/adapters/aitrackdown.py,sha256=XyE0XfhitfW1vmqDS-KtVE7275vyO6FeP0DbbW5Bm4c,17849
6
- mcp_ticketer/adapters/github.py,sha256=Z4lQKvd8jaOHS5zaTz3-GLas90y-HqSvDlQtFhwL-nM,47379
5
+ mcp_ticketer/adapters/aitrackdown.py,sha256=Ecw2SQAGVQs5yMH6m2pj61LxCJsuy-g2bvF8uwTpLUE,22588
6
+ mcp_ticketer/adapters/github.py,sha256=YbZ8hj4nOy4pGIbZ_0zwciNbNnwK69zqfljb-wc2tSY,47384
7
7
  mcp_ticketer/adapters/hybrid.py,sha256=UADYZLc_UNw0xHPSbgguBNzvUCnuYn12Qi9ea-zdlMk,19086
8
- mcp_ticketer/adapters/jira.py,sha256=KyC2BQOCLEmDxZzh06U3F-iNQPqyZQbbnaWqfbaYF8g,35349
8
+ mcp_ticketer/adapters/jira.py,sha256=labZFqOy_mmMmizC-RD1EQbu9m4LLtJywwZ956-_x5E,35347
9
9
  mcp_ticketer/adapters/linear.py,sha256=trm6ZhmlUl80sj51WAPAox_R2HQZXZ-h1QXJsrFYDCQ,587
10
10
  mcp_ticketer/adapters/linear/__init__.py,sha256=6l0ZoR6ZHSRcytLfps2AZuk5R189Pq1GfR5-YDQt8-Q,731
11
- mcp_ticketer/adapters/linear/adapter.py,sha256=iucbQTFgTK0qKGGjIo0nOknMexBw8ZszXLWyehGiHFE,26168
12
- mcp_ticketer/adapters/linear/client.py,sha256=x0yNOJref9IPZGENehhmDPAEhQgj8hpmKFT7WKzdz7Q,8831
13
- mcp_ticketer/adapters/linear/mappers.py,sha256=2ujb83Zlat8RFtG3l-PGXamgOo3lKJpnneWLK9I-m40,9608
11
+ mcp_ticketer/adapters/linear/adapter.py,sha256=IwmMCm9UFss1NcOnf3gdAlBJ8AeiVTG3FbeqW36gpVg,29606
12
+ mcp_ticketer/adapters/linear/client.py,sha256=0UmWlSEcRiwnSMFYKL89KMrPPL8S8uZ5V6rIY_KFOQU,8803
13
+ mcp_ticketer/adapters/linear/mappers.py,sha256=GN1X7bOcU-5dhDW3dAtSEGivinhFBc8hoKYot8c5tCo,9631
14
14
  mcp_ticketer/adapters/linear/queries.py,sha256=LScSwVb9QA6gPiJJ1vHHoTwA6NA3TEPPD7dUzd1zA4g,7196
15
- mcp_ticketer/adapters/linear/types.py,sha256=5XQtAlxRW0sHFqoZ9yP0sGubjkJaG9GV8teZB5k1p8s,7880
15
+ mcp_ticketer/adapters/linear/types.py,sha256=ugXtRGLljDw6yoCnEVgdFs0xLR9ErLdnv4ffh9EAUhk,7874
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=jmXc8clocQi-CJGWhCxG21FgkM5sCD1ZGqEZEBVccV4,14994
19
+ mcp_ticketer/cli/adapter_diagnostics.py,sha256=pQDdtDgBwSW04wdFEPVzwbul3KgfB9g6ZMS85qpYulY,14988
20
20
  mcp_ticketer/cli/auggie_configure.py,sha256=MXKzLtqe3K_UTQ2GacHAWbvf_B0779KL325smiAKE0Q,8212
21
- mcp_ticketer/cli/codex_configure.py,sha256=xDppHouT6_-cYXswyAggoPX5bSlRXMvCoM_x9PQ-42A,9086
21
+ mcp_ticketer/cli/codex_configure.py,sha256=k7-q5d7NICjm7Vxa-z0mejJNAG-bicAHKcwqrzdRDyU,9080
22
22
  mcp_ticketer/cli/configure.py,sha256=BsA_pSHQMQS0t1bJO_wMM8LWsd5sWJDASjEPRHvwC18,16198
23
- mcp_ticketer/cli/diagnostics.py,sha256=Z1rZuSLriR6qg_2F5Y9DBBFE5haB1lpDeqqr0tN65WQ,30034
23
+ mcp_ticketer/cli/diagnostics.py,sha256=jHF68ydW3RNVGumBnHUjUmq6YOjQD2UDkx0O7M__xv0,29965
24
24
  mcp_ticketer/cli/discover.py,sha256=AF_qlQc1Oo0UkWayoF5pmRChS5J3fJjH6f2YZzd_k8w,13188
25
25
  mcp_ticketer/cli/gemini_configure.py,sha256=ZNSA1lBW-itVToza-JxW95Po7daVXKiZAh7lp6pmXMU,9343
26
26
  mcp_ticketer/cli/linear_commands.py,sha256=_8f8ze_1MbiUweU6RFHpldgfHLirysIdPjHr2_S0YhI,17319
27
- mcp_ticketer/cli/main.py,sha256=C06b393NtgRwLEp4CjOFCQQCNEQ9V-UoMMrJy33r_P0,73982
27
+ mcp_ticketer/cli/main.py,sha256=BFo7QYU0tTOHWSfTmegzDqflSYFytCE8Jw6QGB7TwaY,74470
28
28
  mcp_ticketer/cli/mcp_configure.py,sha256=RzV50UjXgOmvMp-9S0zS39psuvjffVByaMrqrUaAGAM,9594
29
29
  mcp_ticketer/cli/migrate_config.py,sha256=MYsr_C5ZxsGg0P13etWTWNrJ_lc6ElRCkzfQADYr3DM,5956
30
30
  mcp_ticketer/cli/queue_commands.py,sha256=mm-3H6jmkUGJDyU_E46o9iRpek8tvFCm77F19OtHiZI,7884
31
- mcp_ticketer/cli/simple_health.py,sha256=d9_dVqtkmxE5n5GvtlXcc5H2rzKBGSnTrsiPc7XWwtk,8192
31
+ mcp_ticketer/cli/simple_health.py,sha256=GlOLRRFoifCna995NoHuKpb3xmFkLi2b3Ke1hyeDvq4,7950
32
32
  mcp_ticketer/cli/utils.py,sha256=IOycMmhwtCDpL3RN_wVEZkYMg9FHyDr4mg5M9Bxzfbo,23041
33
33
  mcp_ticketer/core/__init__.py,sha256=eXovsaJymQRP2AwOBuOy6mFtI3I68D7gGenZ5V-IMqo,349
34
34
  mcp_ticketer/core/adapter.py,sha256=q64LxOInIno7EIbmuxItf8KEsd-g9grCs__Z4uwZHto,10273
35
35
  mcp_ticketer/core/config.py,sha256=ZaooOtim4ZgaQrDPjL5TWJ-K03N9hb0s63YehCsNqR0,19372
36
36
  mcp_ticketer/core/env_discovery.py,sha256=bHOfj7YVExhqFVxto3G-zZ8p6T12BOhF0J4IXCjdll8,19971
37
- mcp_ticketer/core/env_loader.py,sha256=RaLf14ATkpG6XjakYWP7hCpEtsOH3HCRUBC58XSv-MQ,11877
38
- mcp_ticketer/core/exceptions.py,sha256=2fQ-t45FoPAhYYVxhXwdp9OehL50P-b2ReKyFYyc1Ps,3643
37
+ mcp_ticketer/core/env_loader.py,sha256=VLCQhK50quM5e3LkrAGHD5on5vTBj18Z2IgNMNES14M,11866
38
+ mcp_ticketer/core/exceptions.py,sha256=H1gUmNiOjVXn4CT-JLQcGXmjWxHaxxdFvwcpJLTrs-U,3621
39
39
  mcp_ticketer/core/http_client.py,sha256=s5ikMiwEJ8TJjNn73wu3gv3OdAtyBEpAqPnSroRMW2k,13971
40
40
  mcp_ticketer/core/mappers.py,sha256=1aG1jFsHTCwmGRVgOlXW-VOSTGzc86gv7qjDfiR1ups,17462
41
- mcp_ticketer/core/models.py,sha256=O3t7x1Q5l6OYS93HNSg7Kp-d6JapVjdTc6gAAfUBxUo,12495
41
+ mcp_ticketer/core/models.py,sha256=r3BqH0pK2ag2_7c7SSKMZe2G0K7eQID6qy2tkC-GvfI,13155
42
42
  mcp_ticketer/core/project_config.py,sha256=5W9YvDBBASEo5fBcSF-rlA1W3LwzkTv6_CEJXxnsOjc,23346
43
43
  mcp_ticketer/core/registry.py,sha256=ShYLDPE62KFJpB0kj_zFyQzRxSH3LkQEEuo1jaakb1k,3483
44
44
  mcp_ticketer/mcp/__init__.py,sha256=Y05eTzsPk0wH8yKNIM-ekpGjgSDO0bQr0EME-vOP4GE,123
45
- mcp_ticketer/mcp/server.py,sha256=-hZUshZaov2BhcfH5MKnInvHcAX_D2_O6gHHV5YchxI,86397
45
+ mcp_ticketer/mcp/constants.py,sha256=EBGsJtBPaTCvAm5rOMknckrXActrNIls7lRklnh1L4s,2072
46
+ mcp_ticketer/mcp/dto.py,sha256=fUNAdCnPNp80s6RYLFqSmgqQZX04BHYry4GArmFkdG0,7336
47
+ mcp_ticketer/mcp/response_builder.py,sha256=sEYiwQddlfQmIOcbQ-yBsDvH1EJfbTDwCEUJNf7q5Vk,4946
48
+ mcp_ticketer/mcp/server.py,sha256=l9DQ4e9pzdIyIS3Y-gKQwzOzBiVxDi0YJO0U6EeCz5M,46825
46
49
  mcp_ticketer/queue/__init__.py,sha256=1YIaCpZpFqPcqvDEQXiEvDLiw94DXRdCJkBaVIFQrms,231
47
50
  mcp_ticketer/queue/__main__.py,sha256=gc_tE9NUdK07OJfTZuD4t6KeBD_vxFQIhknGTQUG_jk,109
48
- mcp_ticketer/queue/health_monitor.py,sha256=bVKbzzdCOxCrUSmALXiXLn48IKgHYWX72tl_JfaEqqE,12276
49
- mcp_ticketer/queue/manager.py,sha256=zI2xUK0jV8o7j-87RvxG2PD49oJ3roZmmSXMBjL59As,10269
50
- mcp_ticketer/queue/queue.py,sha256=t-qbKoHST_8rPOXVEFOY9MOuU84u9ec5VDtiNHoWO24,17432
51
+ mcp_ticketer/queue/health_monitor.py,sha256=KFOzksomUFnS94XKBiuHFPmGK6b4QXWzsrjwhHkR9vI,12245
52
+ mcp_ticketer/queue/manager.py,sha256=qo7splhIIqzPxINpD8Yflr335sRpuqXtWr2JlsjB8Us,12246
53
+ mcp_ticketer/queue/queue.py,sha256=PIB_8gOE4rCb5_tBNKw9qD6YhSgH3Ei3IzVrUSY3F_o,17978
51
54
  mcp_ticketer/queue/run_worker.py,sha256=WhoeamL8LKZ66TM8W1PkMPwjF2w_EDFMP-mevs6C1TM,1019
52
- mcp_ticketer/queue/ticket_registry.py,sha256=dU0pCa-vshkIqCj_W6dUW1sfNyeB257x1o5tjxjfjw4,15466
53
- mcp_ticketer/queue/worker.py,sha256=Pg-fK8o3d9OHvxCxjh538wDwMQfzLL-X_pCE9lzAOkM,20517
54
- mcp_ticketer-0.3.2.dist-info/licenses/LICENSE,sha256=KOVrunjtILSzY-2N8Lqa3-Q8dMaZIG4LrlLTr9UqL08,1073
55
- mcp_ticketer-0.3.2.dist-info/METADATA,sha256=vh-Xpu_w8aOREJHZKsGFFHxuc5YmbgqukTwY_s2Uk_A,13219
56
- mcp_ticketer-0.3.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
- mcp_ticketer-0.3.2.dist-info/entry_points.txt,sha256=o1IxVhnHnBNG7FZzbFq-Whcs1Djbofs0qMjiUYBLx2s,60
58
- mcp_ticketer-0.3.2.dist-info/top_level.txt,sha256=WnAG4SOT1Vm9tIwl70AbGG_nA217YyV3aWFhxLH2rxw,13
59
- mcp_ticketer-0.3.2.dist-info/RECORD,,
55
+ mcp_ticketer/queue/ticket_registry.py,sha256=FE6W_D8NA-66cJQ6VqghChF3JasYW845JVfEZdiqLbA,15449
56
+ mcp_ticketer/queue/worker.py,sha256=AF6W1bdxWnHiJd6-iBWqTHkZ4lFflsS65CAtgFPR0FA,20983
57
+ mcp_ticketer-0.3.4.dist-info/licenses/LICENSE,sha256=KOVrunjtILSzY-2N8Lqa3-Q8dMaZIG4LrlLTr9UqL08,1073
58
+ mcp_ticketer-0.3.4.dist-info/METADATA,sha256=mcdoktp4RGFdnMy0_fMGrCQXJ72rTjCtVsF3fqrpDOg,13219
59
+ mcp_ticketer-0.3.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
60
+ mcp_ticketer-0.3.4.dist-info/entry_points.txt,sha256=o1IxVhnHnBNG7FZzbFq-Whcs1Djbofs0qMjiUYBLx2s,60
61
+ mcp_ticketer-0.3.4.dist-info/top_level.txt,sha256=WnAG4SOT1Vm9tIwl70AbGG_nA217YyV3aWFhxLH2rxw,13
62
+ mcp_ticketer-0.3.4.dist-info/RECORD,,