mcp-ticketer 0.1.30__py3-none-any.whl → 1.2.11__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 (109) hide show
  1. mcp_ticketer/__init__.py +10 -10
  2. mcp_ticketer/__version__.py +3 -3
  3. mcp_ticketer/adapters/__init__.py +2 -0
  4. mcp_ticketer/adapters/aitrackdown.py +796 -46
  5. mcp_ticketer/adapters/asana/__init__.py +15 -0
  6. mcp_ticketer/adapters/asana/adapter.py +1416 -0
  7. mcp_ticketer/adapters/asana/client.py +292 -0
  8. mcp_ticketer/adapters/asana/mappers.py +348 -0
  9. mcp_ticketer/adapters/asana/types.py +146 -0
  10. mcp_ticketer/adapters/github.py +879 -129
  11. mcp_ticketer/adapters/hybrid.py +11 -11
  12. mcp_ticketer/adapters/jira.py +973 -73
  13. mcp_ticketer/adapters/linear/__init__.py +24 -0
  14. mcp_ticketer/adapters/linear/adapter.py +2732 -0
  15. mcp_ticketer/adapters/linear/client.py +344 -0
  16. mcp_ticketer/adapters/linear/mappers.py +420 -0
  17. mcp_ticketer/adapters/linear/queries.py +479 -0
  18. mcp_ticketer/adapters/linear/types.py +360 -0
  19. mcp_ticketer/adapters/linear.py +10 -2315
  20. mcp_ticketer/analysis/__init__.py +23 -0
  21. mcp_ticketer/analysis/orphaned.py +218 -0
  22. mcp_ticketer/analysis/similarity.py +224 -0
  23. mcp_ticketer/analysis/staleness.py +266 -0
  24. mcp_ticketer/cache/memory.py +9 -8
  25. mcp_ticketer/cli/adapter_diagnostics.py +421 -0
  26. mcp_ticketer/cli/auggie_configure.py +116 -15
  27. mcp_ticketer/cli/codex_configure.py +274 -82
  28. mcp_ticketer/cli/configure.py +888 -151
  29. mcp_ticketer/cli/diagnostics.py +400 -157
  30. mcp_ticketer/cli/discover.py +297 -26
  31. mcp_ticketer/cli/gemini_configure.py +119 -26
  32. mcp_ticketer/cli/init_command.py +880 -0
  33. mcp_ticketer/cli/instruction_commands.py +435 -0
  34. mcp_ticketer/cli/linear_commands.py +616 -0
  35. mcp_ticketer/cli/main.py +203 -1165
  36. mcp_ticketer/cli/mcp_configure.py +474 -90
  37. mcp_ticketer/cli/mcp_server_commands.py +415 -0
  38. mcp_ticketer/cli/migrate_config.py +12 -8
  39. mcp_ticketer/cli/platform_commands.py +123 -0
  40. mcp_ticketer/cli/platform_detection.py +418 -0
  41. mcp_ticketer/cli/platform_installer.py +513 -0
  42. mcp_ticketer/cli/python_detection.py +126 -0
  43. mcp_ticketer/cli/queue_commands.py +15 -15
  44. mcp_ticketer/cli/setup_command.py +639 -0
  45. mcp_ticketer/cli/simple_health.py +90 -65
  46. mcp_ticketer/cli/ticket_commands.py +1013 -0
  47. mcp_ticketer/cli/update_checker.py +313 -0
  48. mcp_ticketer/cli/utils.py +114 -66
  49. mcp_ticketer/core/__init__.py +24 -1
  50. mcp_ticketer/core/adapter.py +250 -16
  51. mcp_ticketer/core/config.py +145 -37
  52. mcp_ticketer/core/env_discovery.py +101 -22
  53. mcp_ticketer/core/env_loader.py +349 -0
  54. mcp_ticketer/core/exceptions.py +160 -0
  55. mcp_ticketer/core/http_client.py +26 -26
  56. mcp_ticketer/core/instructions.py +405 -0
  57. mcp_ticketer/core/label_manager.py +732 -0
  58. mcp_ticketer/core/mappers.py +42 -30
  59. mcp_ticketer/core/models.py +280 -28
  60. mcp_ticketer/core/onepassword_secrets.py +379 -0
  61. mcp_ticketer/core/project_config.py +183 -49
  62. mcp_ticketer/core/registry.py +3 -3
  63. mcp_ticketer/core/session_state.py +171 -0
  64. mcp_ticketer/core/state_matcher.py +592 -0
  65. mcp_ticketer/core/url_parser.py +425 -0
  66. mcp_ticketer/core/validators.py +69 -0
  67. mcp_ticketer/defaults/ticket_instructions.md +644 -0
  68. mcp_ticketer/mcp/__init__.py +29 -1
  69. mcp_ticketer/mcp/__main__.py +60 -0
  70. mcp_ticketer/mcp/server/__init__.py +25 -0
  71. mcp_ticketer/mcp/server/__main__.py +60 -0
  72. mcp_ticketer/mcp/server/constants.py +58 -0
  73. mcp_ticketer/mcp/server/diagnostic_helper.py +175 -0
  74. mcp_ticketer/mcp/server/dto.py +195 -0
  75. mcp_ticketer/mcp/server/main.py +1343 -0
  76. mcp_ticketer/mcp/server/response_builder.py +206 -0
  77. mcp_ticketer/mcp/server/routing.py +655 -0
  78. mcp_ticketer/mcp/server/server_sdk.py +151 -0
  79. mcp_ticketer/mcp/server/tools/__init__.py +56 -0
  80. mcp_ticketer/mcp/server/tools/analysis_tools.py +495 -0
  81. mcp_ticketer/mcp/server/tools/attachment_tools.py +226 -0
  82. mcp_ticketer/mcp/server/tools/bulk_tools.py +273 -0
  83. mcp_ticketer/mcp/server/tools/comment_tools.py +152 -0
  84. mcp_ticketer/mcp/server/tools/config_tools.py +1439 -0
  85. mcp_ticketer/mcp/server/tools/diagnostic_tools.py +211 -0
  86. mcp_ticketer/mcp/server/tools/hierarchy_tools.py +921 -0
  87. mcp_ticketer/mcp/server/tools/instruction_tools.py +300 -0
  88. mcp_ticketer/mcp/server/tools/label_tools.py +948 -0
  89. mcp_ticketer/mcp/server/tools/pr_tools.py +152 -0
  90. mcp_ticketer/mcp/server/tools/search_tools.py +215 -0
  91. mcp_ticketer/mcp/server/tools/session_tools.py +170 -0
  92. mcp_ticketer/mcp/server/tools/ticket_tools.py +1268 -0
  93. mcp_ticketer/mcp/server/tools/user_ticket_tools.py +547 -0
  94. mcp_ticketer/queue/__init__.py +1 -0
  95. mcp_ticketer/queue/health_monitor.py +168 -136
  96. mcp_ticketer/queue/manager.py +95 -25
  97. mcp_ticketer/queue/queue.py +40 -21
  98. mcp_ticketer/queue/run_worker.py +6 -1
  99. mcp_ticketer/queue/ticket_registry.py +213 -155
  100. mcp_ticketer/queue/worker.py +109 -49
  101. mcp_ticketer-1.2.11.dist-info/METADATA +792 -0
  102. mcp_ticketer-1.2.11.dist-info/RECORD +110 -0
  103. mcp_ticketer/mcp/server.py +0 -1895
  104. mcp_ticketer-0.1.30.dist-info/METADATA +0 -413
  105. mcp_ticketer-0.1.30.dist-info/RECORD +0 -49
  106. {mcp_ticketer-0.1.30.dist-info → mcp_ticketer-1.2.11.dist-info}/WHEEL +0 -0
  107. {mcp_ticketer-0.1.30.dist-info → mcp_ticketer-1.2.11.dist-info}/entry_points.txt +0 -0
  108. {mcp_ticketer-0.1.30.dist-info → mcp_ticketer-1.2.11.dist-info}/licenses/LICENSE +0 -0
  109. {mcp_ticketer-0.1.30.dist-info → mcp_ticketer-1.2.11.dist-info}/top_level.txt +0 -0
mcp_ticketer/cli/utils.py CHANGED
@@ -4,9 +4,10 @@ import asyncio
4
4
  import json
5
5
  import logging
6
6
  import os
7
+ from collections.abc import Callable
7
8
  from functools import wraps
8
9
  from pathlib import Path
9
- from typing import Any, Callable, Optional, TypeVar
10
+ from typing import Any, TypeVar
10
11
 
11
12
  import typer
12
13
  from rich.console import Console
@@ -31,7 +32,7 @@ class CommonPatterns:
31
32
 
32
33
  @staticmethod
33
34
  def load_config() -> dict:
34
- """Load configuration from project-local config file ONLY.
35
+ """Load configuration from project-local config file with environment discovery fallback.
35
36
 
36
37
  SECURITY: This method ONLY reads from the current project directory
37
38
  to prevent configuration leakage across projects. It will NEVER read
@@ -39,7 +40,8 @@ class CommonPatterns:
39
40
 
40
41
  Resolution order:
41
42
  1. Project-specific config (.mcp-ticketer/config.json in cwd)
42
- 2. Default to aitrackdown adapter
43
+ 2. Environment discovery (environment variables and .env files in cwd)
44
+ 3. Default to aitrackdown adapter
43
45
 
44
46
  Returns:
45
47
  Configuration dictionary with adapter and config keys.
@@ -77,6 +79,43 @@ class CommonPatterns:
77
79
  f"[yellow]Warning: Could not load project config: {e}[/yellow]"
78
80
  )
79
81
 
82
+ # Try environment discovery as fallback
83
+ try:
84
+ from ..core.config import ConfigurationManager
85
+
86
+ config_manager = ConfigurationManager()
87
+ app_config = config_manager.load_config()
88
+
89
+ # Convert AppConfig to legacy dict format for CLI compatibility
90
+ enabled_adapters = app_config.get_enabled_adapters()
91
+ if enabled_adapters:
92
+ # Use the first enabled adapter as default
93
+ default_adapter = (
94
+ app_config.default_adapter or list(enabled_adapters.keys())[0]
95
+ )
96
+
97
+ # Convert to legacy format
98
+ legacy_config = {"default_adapter": default_adapter, "adapters": {}}
99
+
100
+ # Convert adapter configs to dict format
101
+ for name, adapter_config in enabled_adapters.items():
102
+ if hasattr(adapter_config, "model_dump"):
103
+ legacy_config["adapters"][name] = adapter_config.model_dump(
104
+ exclude_none=False
105
+ )
106
+ elif hasattr(adapter_config, "dict"):
107
+ legacy_config["adapters"][name] = adapter_config.dict()
108
+ else:
109
+ legacy_config["adapters"][name] = adapter_config
110
+
111
+ logger.info(
112
+ f"Loaded configuration from environment discovery: {list(enabled_adapters.keys())}"
113
+ )
114
+ return legacy_config
115
+
116
+ except Exception as e:
117
+ logger.warning(f"Environment discovery failed: {e}")
118
+
80
119
  # Default to aitrackdown with local base path
81
120
  logger.info("No project-local config found, defaulting to aitrackdown adapter")
82
121
  return {"adapter": "aitrackdown", "config": {"base_path": ".aitrackdown"}}
@@ -116,8 +155,8 @@ class CommonPatterns:
116
155
 
117
156
  @staticmethod
118
157
  def get_adapter(
119
- override_adapter: Optional[str] = None, override_config: Optional[dict] = None
120
- ):
158
+ override_adapter: str | None = None, override_config: dict | None = None
159
+ ) -> Any:
121
160
  """Get configured adapter instance with environment variable support."""
122
161
  config = CommonPatterns.load_config()
123
162
 
@@ -168,7 +207,7 @@ class CommonPatterns:
168
207
  def queue_operation(
169
208
  ticket_data: dict[str, Any],
170
209
  operation: str,
171
- adapter_name: Optional[str] = None,
210
+ adapter_name: str | None = None,
172
211
  show_progress: bool = True,
173
212
  ) -> str:
174
213
  """Queue an operation and optionally start the worker."""
@@ -176,10 +215,15 @@ class CommonPatterns:
176
215
  config = CommonPatterns.load_config()
177
216
  adapter_name = config.get("default_adapter", "aitrackdown")
178
217
 
179
- # Add to queue
218
+ # Add to queue with explicit project directory
219
+ from pathlib import Path
220
+
180
221
  queue = Queue()
181
222
  queue_id = queue.add(
182
- ticket_data=ticket_data, adapter=adapter_name, operation=operation
223
+ ticket_data=ticket_data,
224
+ adapter=adapter_name,
225
+ operation=operation,
226
+ project_dir=str(Path.cwd()), # Explicitly pass current project directory
183
227
  )
184
228
 
185
229
  if show_progress:
@@ -222,7 +266,7 @@ class CommonPatterns:
222
266
  console.print(table)
223
267
 
224
268
  @staticmethod
225
- def display_ticket_details(ticket: Task, comments: Optional[list] = None) -> None:
269
+ def display_ticket_details(ticket: Task, comments: list | None = None) -> None:
226
270
  """Display detailed ticket information."""
227
271
  console.print(f"\n[bold]Ticket: {ticket.id}[/bold]")
228
272
  console.print(f"Title: {ticket.title}")
@@ -277,33 +321,35 @@ class CommonPatterns:
277
321
  )
278
322
 
279
323
 
280
- def async_command(f: Callable[..., T]) -> Callable[..., T]:
281
- """Decorator to handle async CLI commands."""
324
+ def async_command(f: Callable[..., Any]) -> Callable[..., Any]:
325
+ """Handle async CLI commands via decorator."""
282
326
 
283
327
  @wraps(f)
284
- def wrapper(*args, **kwargs):
328
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
285
329
  return asyncio.run(f(*args, **kwargs))
286
330
 
287
331
  return wrapper
288
332
 
289
333
 
290
- def with_adapter(f: Callable) -> Callable:
291
- """Decorator to inject adapter instance into CLI commands."""
334
+ def with_adapter(f: Callable[..., Any]) -> Callable[..., Any]:
335
+ """Inject adapter instance into CLI commands via decorator."""
292
336
 
293
337
  @wraps(f)
294
- def wrapper(adapter: Optional[str] = None, *args, **kwargs):
338
+ def wrapper(adapter: str | None = None, *args: Any, **kwargs: Any) -> Any:
295
339
  adapter_instance = CommonPatterns.get_adapter(override_adapter=adapter)
296
340
  return f(adapter_instance, *args, **kwargs)
297
341
 
298
342
  return wrapper
299
343
 
300
344
 
301
- def with_progress(message: str = "Processing..."):
302
- """Decorator to show progress spinner for long-running operations."""
345
+ def with_progress(
346
+ message: str = "Processing...",
347
+ ) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
348
+ """Show progress spinner for long-running operations via decorator."""
303
349
 
304
- def decorator(f: Callable) -> Callable:
350
+ def decorator(f: Callable[..., Any]) -> Callable[..., Any]:
305
351
  @wraps(f)
306
- def wrapper(*args, **kwargs):
352
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
307
353
  with Progress(
308
354
  SpinnerColumn(),
309
355
  TextColumn("[progress.description]{task.description}"),
@@ -317,12 +363,14 @@ def with_progress(message: str = "Processing..."):
317
363
  return decorator
318
364
 
319
365
 
320
- def validate_required_fields(**field_map):
321
- """Decorator to validate required fields are provided."""
366
+ def validate_required_fields(
367
+ **field_map: str,
368
+ ) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
369
+ """Validate required fields are provided via decorator."""
322
370
 
323
- def decorator(f: Callable) -> Callable:
371
+ def decorator(f: Callable[..., Any]) -> Callable[..., Any]:
324
372
  @wraps(f)
325
- def wrapper(*args, **kwargs):
373
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
326
374
  missing_fields = []
327
375
  for field_name, display_name in field_map.items():
328
376
  if field_name in kwargs and kwargs[field_name] is None:
@@ -332,7 +380,7 @@ def validate_required_fields(**field_map):
332
380
  console.print(
333
381
  f"[red]Error:[/red] Missing required fields: {', '.join(missing_fields)}"
334
382
  )
335
- raise typer.Exit(1)
383
+ raise typer.Exit(1) from None
336
384
 
337
385
  return f(*args, **kwargs)
338
386
 
@@ -341,24 +389,24 @@ def validate_required_fields(**field_map):
341
389
  return decorator
342
390
 
343
391
 
344
- def handle_adapter_errors(f: Callable) -> Callable:
345
- """Decorator to handle common adapter errors gracefully."""
392
+ def handle_adapter_errors(f: Callable[..., Any]) -> Callable[..., Any]:
393
+ """Handle common adapter errors gracefully via decorator."""
346
394
 
347
395
  @wraps(f)
348
- def wrapper(*args, **kwargs):
396
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
349
397
  try:
350
398
  return f(*args, **kwargs)
351
399
  except ConnectionError as e:
352
400
  console.print(f"[red]Connection Error:[/red] {e}")
353
401
  console.print("Check your network connection and adapter configuration")
354
- raise typer.Exit(1)
402
+ raise typer.Exit(1) from None
355
403
  except ValueError as e:
356
404
  console.print(f"[red]Configuration Error:[/red] {e}")
357
405
  console.print("Run 'mcp-ticketer init' to configure your adapter")
358
- raise typer.Exit(1)
406
+ raise typer.Exit(1) from None
359
407
  except Exception as e:
360
408
  console.print(f"[red]Unexpected Error:[/red] {e}")
361
- raise typer.Exit(1)
409
+ raise typer.Exit(1) from None
362
410
 
363
411
  return wrapper
364
412
 
@@ -403,7 +451,7 @@ class ConfigValidator:
403
451
  return issues
404
452
 
405
453
  @staticmethod
406
- def _get_env_var(adapter_type: str, field: str) -> Optional[str]:
454
+ def _get_env_var(adapter_type: str, field: str) -> str | None:
407
455
  """Get corresponding environment variable name for a config field."""
408
456
  env_mapping = {
409
457
  "github": {
@@ -424,39 +472,39 @@ class ConfigValidator:
424
472
  class CommandBuilder:
425
473
  """Builder for common CLI command patterns."""
426
474
 
427
- def __init__(self):
428
- self._validators = []
429
- self._handlers = []
430
- self._decorators = []
475
+ def __init__(self) -> None:
476
+ self._validators: list[Callable[..., Any]] = []
477
+ self._handlers: list[Callable[..., Any]] = []
478
+ self._decorators: list[Callable[..., Any]] = []
431
479
 
432
- def with_adapter_validation(self):
480
+ def with_adapter_validation(self) -> "CommandBuilder":
433
481
  """Add adapter configuration validation."""
434
482
  self._validators.append(self._validate_adapter)
435
483
  return self
436
484
 
437
- def with_async_support(self):
485
+ def with_async_support(self) -> "CommandBuilder":
438
486
  """Add async support to command."""
439
487
  self._decorators.append(async_command)
440
488
  return self
441
489
 
442
- def with_error_handling(self):
490
+ def with_error_handling(self) -> "CommandBuilder":
443
491
  """Add error handling decorator."""
444
492
  self._decorators.append(handle_adapter_errors)
445
493
  return self
446
494
 
447
- def with_progress(self, message: str = "Processing..."):
495
+ def with_progress(self, message: str = "Processing...") -> "CommandBuilder":
448
496
  """Add progress spinner."""
449
497
  self._decorators.append(with_progress(message))
450
498
  return self
451
499
 
452
- def build(self, func: Callable) -> Callable:
500
+ def build(self, func: Callable[..., Any]) -> Callable[..., Any]:
453
501
  """Build the decorated function."""
454
502
  decorated_func = func
455
503
  for decorator in reversed(self._decorators):
456
504
  decorated_func = decorator(decorated_func)
457
505
  return decorated_func
458
506
 
459
- def _validate_adapter(self, *args, **kwargs):
507
+ def _validate_adapter(self, *args: Any, **kwargs: Any) -> None:
460
508
  """Validate adapter configuration."""
461
509
  config = CommonPatterns.load_config()
462
510
  default_adapter = config.get("default_adapter", "aitrackdown")
@@ -470,22 +518,22 @@ class CommandBuilder:
470
518
  for issue in issues:
471
519
  console.print(f" • {issue}")
472
520
  console.print("Run 'mcp-ticketer init' to fix configuration")
473
- raise typer.Exit(1)
521
+ raise typer.Exit(1) from None
474
522
 
475
523
 
476
- def create_standard_ticket_command(operation: str):
524
+ def create_standard_ticket_command(operation: str) -> Callable[..., str]:
477
525
  """Create a standard ticket operation command."""
478
526
 
479
527
  def command_template(
480
- ticket_id: Optional[str] = None,
481
- title: Optional[str] = None,
482
- description: Optional[str] = None,
483
- priority: Optional[Priority] = None,
484
- state: Optional[TicketState] = None,
485
- assignee: Optional[str] = None,
486
- tags: Optional[list[str]] = None,
487
- adapter: Optional[str] = None,
488
- ):
528
+ ticket_id: str | None = None,
529
+ title: str | None = None,
530
+ description: str | None = None,
531
+ priority: Priority | None = None,
532
+ state: TicketState | None = None,
533
+ assignee: str | None = None,
534
+ tags: list[str] | None = None,
535
+ adapter: str | None = None,
536
+ ) -> str:
489
537
  """Template for ticket commands."""
490
538
  # Build ticket data
491
539
  ticket_data = {}
@@ -524,11 +572,11 @@ class TicketCommands:
524
572
  @async_command
525
573
  @handle_adapter_errors
526
574
  async def list_tickets(
527
- adapter_instance,
528
- state: Optional[TicketState] = None,
529
- priority: Optional[Priority] = None,
575
+ adapter_instance: Any,
576
+ state: TicketState | None = None,
577
+ priority: Priority | None = None,
530
578
  limit: int = 10,
531
- ):
579
+ ) -> None:
532
580
  """List tickets with filters."""
533
581
  filters = {}
534
582
  if state:
@@ -543,13 +591,13 @@ class TicketCommands:
543
591
  @async_command
544
592
  @handle_adapter_errors
545
593
  async def show_ticket(
546
- adapter_instance, ticket_id: str, show_comments: bool = False
547
- ):
594
+ adapter_instance: Any, ticket_id: str, show_comments: bool = False
595
+ ) -> None:
548
596
  """Show ticket details."""
549
597
  ticket = await adapter_instance.read(ticket_id)
550
598
  if not ticket:
551
599
  console.print(f"[red]✗[/red] Ticket not found: {ticket_id}")
552
- raise typer.Exit(1)
600
+ raise typer.Exit(1) from None
553
601
 
554
602
  comments = None
555
603
  if show_comments:
@@ -560,11 +608,11 @@ class TicketCommands:
560
608
  @staticmethod
561
609
  def create_ticket(
562
610
  title: str,
563
- description: Optional[str] = None,
611
+ description: str | None = None,
564
612
  priority: Priority = Priority.MEDIUM,
565
- tags: Optional[list[str]] = None,
566
- assignee: Optional[str] = None,
567
- adapter: Optional[str] = None,
613
+ tags: list[str] | None = None,
614
+ assignee: str | None = None,
615
+ adapter: str | None = None,
568
616
  ) -> str:
569
617
  """Create a new ticket."""
570
618
  ticket_data = {
@@ -579,19 +627,19 @@ class TicketCommands:
579
627
 
580
628
  @staticmethod
581
629
  def update_ticket(
582
- ticket_id: str, updates: dict[str, Any], adapter: Optional[str] = None
630
+ ticket_id: str, updates: dict[str, Any], adapter: str | None = None
583
631
  ) -> str:
584
632
  """Update a ticket."""
585
633
  if not updates:
586
634
  console.print("[yellow]No updates specified[/yellow]")
587
- raise typer.Exit(1)
635
+ raise typer.Exit(1) from None
588
636
 
589
637
  updates["ticket_id"] = ticket_id
590
638
  return CommonPatterns.queue_operation(updates, "update", adapter)
591
639
 
592
640
  @staticmethod
593
641
  def transition_ticket(
594
- ticket_id: str, state: TicketState, adapter: Optional[str] = None
642
+ ticket_id: str, state: TicketState, adapter: str | None = None
595
643
  ) -> str:
596
644
  """Transition ticket state."""
597
645
  ticket_data = {
@@ -1,16 +1,39 @@
1
1
  """Core models and abstractions for MCP Ticketer."""
2
2
 
3
3
  from .adapter import BaseAdapter
4
- from .models import Comment, Epic, Priority, Task, TicketState, TicketType
4
+ from .instructions import (
5
+ InstructionsError,
6
+ InstructionsNotFoundError,
7
+ InstructionsValidationError,
8
+ TicketInstructionsManager,
9
+ get_instructions,
10
+ )
11
+ from .models import Attachment, Comment, Epic, Priority, Task, TicketState, TicketType
5
12
  from .registry import AdapterRegistry
13
+ from .state_matcher import (
14
+ SemanticStateMatcher,
15
+ StateMatchResult,
16
+ ValidationResult,
17
+ get_state_matcher,
18
+ )
6
19
 
7
20
  __all__ = [
8
21
  "Epic",
9
22
  "Task",
10
23
  "Comment",
24
+ "Attachment",
11
25
  "TicketState",
12
26
  "Priority",
13
27
  "TicketType",
14
28
  "BaseAdapter",
15
29
  "AdapterRegistry",
30
+ "TicketInstructionsManager",
31
+ "InstructionsError",
32
+ "InstructionsNotFoundError",
33
+ "InstructionsValidationError",
34
+ "get_instructions",
35
+ "SemanticStateMatcher",
36
+ "StateMatchResult",
37
+ "ValidationResult",
38
+ "get_state_matcher",
16
39
  ]