mcp-ticketer 0.12.0__py3-none-any.whl → 2.0.1__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.
- mcp_ticketer/__init__.py +10 -10
- mcp_ticketer/__version__.py +1 -1
- mcp_ticketer/adapters/aitrackdown.py +385 -6
- mcp_ticketer/adapters/asana/adapter.py +108 -0
- mcp_ticketer/adapters/asana/mappers.py +14 -0
- mcp_ticketer/adapters/github.py +525 -11
- mcp_ticketer/adapters/hybrid.py +47 -5
- mcp_ticketer/adapters/jira.py +521 -0
- mcp_ticketer/adapters/linear/adapter.py +1784 -101
- mcp_ticketer/adapters/linear/client.py +85 -3
- mcp_ticketer/adapters/linear/mappers.py +96 -8
- mcp_ticketer/adapters/linear/queries.py +168 -1
- mcp_ticketer/adapters/linear/types.py +80 -4
- mcp_ticketer/analysis/__init__.py +56 -0
- mcp_ticketer/analysis/dependency_graph.py +255 -0
- mcp_ticketer/analysis/health_assessment.py +304 -0
- mcp_ticketer/analysis/orphaned.py +218 -0
- mcp_ticketer/analysis/project_status.py +594 -0
- mcp_ticketer/analysis/similarity.py +224 -0
- mcp_ticketer/analysis/staleness.py +266 -0
- mcp_ticketer/automation/__init__.py +11 -0
- mcp_ticketer/automation/project_updates.py +378 -0
- mcp_ticketer/cli/adapter_diagnostics.py +3 -1
- mcp_ticketer/cli/auggie_configure.py +17 -5
- mcp_ticketer/cli/codex_configure.py +97 -61
- mcp_ticketer/cli/configure.py +851 -103
- mcp_ticketer/cli/cursor_configure.py +314 -0
- mcp_ticketer/cli/diagnostics.py +13 -12
- mcp_ticketer/cli/discover.py +5 -0
- mcp_ticketer/cli/gemini_configure.py +17 -5
- mcp_ticketer/cli/init_command.py +880 -0
- mcp_ticketer/cli/instruction_commands.py +6 -0
- mcp_ticketer/cli/main.py +233 -3151
- mcp_ticketer/cli/mcp_configure.py +672 -98
- mcp_ticketer/cli/mcp_server_commands.py +415 -0
- mcp_ticketer/cli/platform_detection.py +77 -12
- mcp_ticketer/cli/platform_installer.py +536 -0
- mcp_ticketer/cli/project_update_commands.py +350 -0
- mcp_ticketer/cli/setup_command.py +639 -0
- mcp_ticketer/cli/simple_health.py +12 -10
- mcp_ticketer/cli/ticket_commands.py +264 -24
- mcp_ticketer/core/__init__.py +28 -6
- mcp_ticketer/core/adapter.py +166 -1
- mcp_ticketer/core/config.py +21 -21
- mcp_ticketer/core/exceptions.py +7 -1
- mcp_ticketer/core/label_manager.py +732 -0
- mcp_ticketer/core/mappers.py +31 -19
- mcp_ticketer/core/models.py +135 -0
- mcp_ticketer/core/onepassword_secrets.py +1 -1
- mcp_ticketer/core/priority_matcher.py +463 -0
- mcp_ticketer/core/project_config.py +132 -14
- mcp_ticketer/core/session_state.py +171 -0
- mcp_ticketer/core/state_matcher.py +592 -0
- mcp_ticketer/core/url_parser.py +425 -0
- mcp_ticketer/core/validators.py +69 -0
- mcp_ticketer/mcp/server/diagnostic_helper.py +175 -0
- mcp_ticketer/mcp/server/main.py +106 -25
- mcp_ticketer/mcp/server/routing.py +655 -0
- mcp_ticketer/mcp/server/server_sdk.py +58 -0
- mcp_ticketer/mcp/server/tools/__init__.py +31 -12
- mcp_ticketer/mcp/server/tools/analysis_tools.py +854 -0
- mcp_ticketer/mcp/server/tools/attachment_tools.py +6 -8
- mcp_ticketer/mcp/server/tools/bulk_tools.py +259 -202
- mcp_ticketer/mcp/server/tools/comment_tools.py +74 -12
- mcp_ticketer/mcp/server/tools/config_tools.py +1184 -136
- mcp_ticketer/mcp/server/tools/diagnostic_tools.py +211 -0
- mcp_ticketer/mcp/server/tools/hierarchy_tools.py +870 -460
- mcp_ticketer/mcp/server/tools/instruction_tools.py +7 -5
- mcp_ticketer/mcp/server/tools/label_tools.py +942 -0
- mcp_ticketer/mcp/server/tools/pr_tools.py +3 -7
- mcp_ticketer/mcp/server/tools/project_status_tools.py +158 -0
- mcp_ticketer/mcp/server/tools/project_update_tools.py +473 -0
- mcp_ticketer/mcp/server/tools/search_tools.py +180 -97
- mcp_ticketer/mcp/server/tools/session_tools.py +308 -0
- mcp_ticketer/mcp/server/tools/ticket_tools.py +1070 -123
- mcp_ticketer/mcp/server/tools/user_ticket_tools.py +218 -236
- mcp_ticketer/queue/worker.py +1 -1
- mcp_ticketer/utils/__init__.py +5 -0
- mcp_ticketer/utils/token_utils.py +246 -0
- mcp_ticketer-2.0.1.dist-info/METADATA +1366 -0
- mcp_ticketer-2.0.1.dist-info/RECORD +122 -0
- mcp_ticketer-0.12.0.dist-info/METADATA +0 -550
- mcp_ticketer-0.12.0.dist-info/RECORD +0 -91
- {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.0.1.dist-info}/WHEEL +0 -0
- {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.0.1.dist-info}/entry_points.txt +0 -0
- {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.0.1.dist-info}/licenses/LICENSE +0 -0
- {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.0.1.dist-info}/top_level.txt +0 -0
|
@@ -473,51 +473,122 @@ def list_tickets(
|
|
|
473
473
|
@app.command()
|
|
474
474
|
def show(
|
|
475
475
|
ticket_id: str = typer.Argument(..., help="Ticket ID"),
|
|
476
|
-
|
|
476
|
+
no_comments: bool = typer.Option(
|
|
477
|
+
False, "--no-comments", help="Hide comments (shown by default)"
|
|
478
|
+
),
|
|
477
479
|
adapter: AdapterType | None = typer.Option(
|
|
478
480
|
None, "--adapter", help="Override default adapter"
|
|
479
481
|
),
|
|
480
482
|
) -> None:
|
|
481
|
-
"""Show detailed ticket information.
|
|
483
|
+
"""Show detailed ticket information with full context.
|
|
484
|
+
|
|
485
|
+
By default, displays ticket details along with all comments to provide
|
|
486
|
+
a holistic view of the ticket's history and context.
|
|
482
487
|
|
|
483
|
-
|
|
488
|
+
Use --no-comments to display only ticket metadata without comments.
|
|
489
|
+
"""
|
|
490
|
+
|
|
491
|
+
async def _show() -> tuple[Any, Any, Any]:
|
|
484
492
|
adapter_instance = get_adapter(
|
|
485
493
|
override_adapter=adapter.value if adapter else None
|
|
486
494
|
)
|
|
487
495
|
ticket = await adapter_instance.read(ticket_id)
|
|
488
496
|
ticket_comments = None
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
497
|
+
attachments = None
|
|
498
|
+
|
|
499
|
+
# Fetch comments by default (unless explicitly disabled)
|
|
500
|
+
if not no_comments and ticket:
|
|
501
|
+
try:
|
|
502
|
+
ticket_comments = await adapter_instance.get_comments(ticket_id)
|
|
503
|
+
except (NotImplementedError, AttributeError):
|
|
504
|
+
# Adapter doesn't support comments
|
|
505
|
+
pass
|
|
506
|
+
|
|
507
|
+
# Try to fetch attachments if available
|
|
508
|
+
if ticket and hasattr(adapter_instance, "list_attachments"):
|
|
509
|
+
try:
|
|
510
|
+
attachments = await adapter_instance.list_attachments(ticket_id)
|
|
511
|
+
except (NotImplementedError, AttributeError):
|
|
512
|
+
pass
|
|
513
|
+
|
|
514
|
+
return ticket, ticket_comments, attachments
|
|
492
515
|
|
|
493
|
-
ticket, ticket_comments = asyncio.run(_show())
|
|
516
|
+
ticket, ticket_comments, attachments = asyncio.run(_show())
|
|
494
517
|
|
|
495
518
|
if not ticket:
|
|
496
519
|
console.print(f"[red]✗[/red] Ticket not found: {ticket_id}")
|
|
497
520
|
raise typer.Exit(1) from None
|
|
498
521
|
|
|
499
|
-
# Display ticket
|
|
500
|
-
console.print(f"\n[bold]Ticket: {ticket.id}[/bold]")
|
|
501
|
-
console.print(f"
|
|
502
|
-
console.print(
|
|
503
|
-
console.print(f"Priority: [yellow]{ticket.priority}[/yellow]")
|
|
522
|
+
# Display ticket header with metadata
|
|
523
|
+
console.print(f"\n[bold cyan]┌─ Ticket: {ticket.id}[/bold cyan]")
|
|
524
|
+
console.print(f"[bold]│ {ticket.title}[/bold]")
|
|
525
|
+
console.print("└" + "─" * 60)
|
|
504
526
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
if ticket.tags:
|
|
510
|
-
console.print(f"\nTags: {', '.join(ticket.tags)}")
|
|
527
|
+
# Display metadata in organized sections
|
|
528
|
+
console.print("\n[bold]Status[/bold]")
|
|
529
|
+
console.print(f" State: [green]{ticket.state}[/green]")
|
|
530
|
+
console.print(f" Priority: [yellow]{ticket.priority}[/yellow]")
|
|
511
531
|
|
|
512
532
|
if ticket.assignee:
|
|
513
|
-
console.print(f"Assignee: {ticket.assignee}")
|
|
533
|
+
console.print(f" Assignee: {ticket.assignee}")
|
|
514
534
|
|
|
515
|
-
# Display
|
|
535
|
+
# Display timestamps if available
|
|
536
|
+
if ticket.created_at or ticket.updated_at:
|
|
537
|
+
console.print("\n[bold]Timeline[/bold]")
|
|
538
|
+
if ticket.created_at:
|
|
539
|
+
console.print(f" Created: {ticket.created_at}")
|
|
540
|
+
if ticket.updated_at:
|
|
541
|
+
console.print(f" Updated: {ticket.updated_at}")
|
|
542
|
+
|
|
543
|
+
# Display tags
|
|
544
|
+
if ticket.tags:
|
|
545
|
+
console.print("\n[bold]Tags[/bold]")
|
|
546
|
+
console.print(f" {', '.join(ticket.tags)}")
|
|
547
|
+
|
|
548
|
+
# Display description
|
|
549
|
+
if ticket.description:
|
|
550
|
+
console.print("\n[bold]Description[/bold]")
|
|
551
|
+
console.print(f" {ticket.description}")
|
|
552
|
+
|
|
553
|
+
# Display parent/child relationships
|
|
554
|
+
parent_info = []
|
|
555
|
+
if hasattr(ticket, "parent_epic") and ticket.parent_epic:
|
|
556
|
+
parent_info.append(f"Epic: {ticket.parent_epic}")
|
|
557
|
+
if hasattr(ticket, "parent_issue") and ticket.parent_issue:
|
|
558
|
+
parent_info.append(f"Parent Issue: {ticket.parent_issue}")
|
|
559
|
+
|
|
560
|
+
if parent_info:
|
|
561
|
+
console.print("\n[bold]Hierarchy[/bold]")
|
|
562
|
+
for info in parent_info:
|
|
563
|
+
console.print(f" {info}")
|
|
564
|
+
|
|
565
|
+
# Display attachments if available
|
|
566
|
+
if attachments and len(attachments) > 0:
|
|
567
|
+
console.print(f"\n[bold]Attachments ({len(attachments)})[/bold]")
|
|
568
|
+
for att in attachments:
|
|
569
|
+
att_title = att.get("title", "Untitled")
|
|
570
|
+
att_url = att.get("url", "")
|
|
571
|
+
console.print(f" 📎 {att_title}")
|
|
572
|
+
if att_url:
|
|
573
|
+
console.print(f" {att_url}")
|
|
574
|
+
|
|
575
|
+
# Display comments with enhanced formatting
|
|
516
576
|
if ticket_comments:
|
|
517
|
-
console.print(f"\n[bold]Comments ({len(ticket_comments)})
|
|
518
|
-
for comment in ticket_comments:
|
|
519
|
-
|
|
520
|
-
|
|
577
|
+
console.print(f"\n[bold]Activity & Comments ({len(ticket_comments)})[/bold]")
|
|
578
|
+
for i, comment in enumerate(ticket_comments, 1):
|
|
579
|
+
# Format timestamp
|
|
580
|
+
timestamp = comment.created_at if comment.created_at else "Unknown time"
|
|
581
|
+
author = comment.author if comment.author else "Unknown author"
|
|
582
|
+
|
|
583
|
+
console.print(f"\n[dim] {i}. {timestamp}[/dim]")
|
|
584
|
+
console.print(f" [cyan]@{author}[/cyan]")
|
|
585
|
+
console.print(f" {comment.content}")
|
|
586
|
+
|
|
587
|
+
# Footer with hint
|
|
588
|
+
if no_comments:
|
|
589
|
+
console.print(
|
|
590
|
+
"\n[dim]💡 Tip: Remove --no-comments to see activity and comments[/dim]"
|
|
591
|
+
)
|
|
521
592
|
|
|
522
593
|
|
|
523
594
|
@app.command()
|
|
@@ -556,6 +627,175 @@ def comment(
|
|
|
556
627
|
raise typer.Exit(1) from None
|
|
557
628
|
|
|
558
629
|
|
|
630
|
+
@app.command()
|
|
631
|
+
def attach(
|
|
632
|
+
ticket_id: str = typer.Argument(..., help="Ticket ID or URL"),
|
|
633
|
+
file_path: Path = typer.Argument(..., help="Path to file to attach", exists=True),
|
|
634
|
+
description: str | None = typer.Option(
|
|
635
|
+
None, "--description", "-d", help="Attachment description or comment"
|
|
636
|
+
),
|
|
637
|
+
adapter: AdapterType | None = typer.Option(
|
|
638
|
+
None, "--adapter", help="Override default adapter"
|
|
639
|
+
),
|
|
640
|
+
) -> None:
|
|
641
|
+
"""Attach a file to a ticket.
|
|
642
|
+
|
|
643
|
+
Examples:
|
|
644
|
+
mcp-ticketer ticket attach 1M-157 docs/analysis.md
|
|
645
|
+
mcp-ticketer ticket attach PROJ-123 screenshot.png -d "Error screenshot"
|
|
646
|
+
mcp-ticketer ticket attach https://linear.app/.../issue/ABC-123 diagram.pdf
|
|
647
|
+
"""
|
|
648
|
+
|
|
649
|
+
async def _attach() -> dict[str, Any]:
|
|
650
|
+
import mimetypes
|
|
651
|
+
|
|
652
|
+
adapter_instance = get_adapter(
|
|
653
|
+
override_adapter=adapter.value if adapter else None
|
|
654
|
+
)
|
|
655
|
+
|
|
656
|
+
# Detect MIME type
|
|
657
|
+
mime_type, _ = mimetypes.guess_type(str(file_path))
|
|
658
|
+
if not mime_type:
|
|
659
|
+
mime_type = "application/octet-stream"
|
|
660
|
+
|
|
661
|
+
# Method 1: Try Linear-specific upload (if available)
|
|
662
|
+
if hasattr(adapter_instance, "upload_file") and hasattr(
|
|
663
|
+
adapter_instance, "attach_file_to_issue"
|
|
664
|
+
):
|
|
665
|
+
try:
|
|
666
|
+
# Upload file to Linear's S3
|
|
667
|
+
file_url = await adapter_instance.upload_file(
|
|
668
|
+
file_path=str(file_path), mime_type=mime_type
|
|
669
|
+
)
|
|
670
|
+
|
|
671
|
+
# Attach to issue
|
|
672
|
+
attachment = await adapter_instance.attach_file_to_issue(
|
|
673
|
+
issue_id=ticket_id,
|
|
674
|
+
file_url=file_url,
|
|
675
|
+
title=file_path.name,
|
|
676
|
+
subtitle=description,
|
|
677
|
+
)
|
|
678
|
+
|
|
679
|
+
return {
|
|
680
|
+
"status": "completed",
|
|
681
|
+
"attachment": attachment,
|
|
682
|
+
"file_url": file_url,
|
|
683
|
+
"method": "linear_native_upload",
|
|
684
|
+
}
|
|
685
|
+
except Exception:
|
|
686
|
+
# If Linear upload fails, fall through to next method
|
|
687
|
+
pass
|
|
688
|
+
|
|
689
|
+
# Method 2: Try generic add_attachment (if available)
|
|
690
|
+
if hasattr(adapter_instance, "add_attachment"):
|
|
691
|
+
try:
|
|
692
|
+
attachment = await adapter_instance.add_attachment(
|
|
693
|
+
ticket_id=ticket_id,
|
|
694
|
+
file_path=str(file_path),
|
|
695
|
+
description=description or "",
|
|
696
|
+
)
|
|
697
|
+
return {
|
|
698
|
+
"status": "completed",
|
|
699
|
+
"attachment": attachment,
|
|
700
|
+
"method": "adapter_native",
|
|
701
|
+
}
|
|
702
|
+
except NotImplementedError:
|
|
703
|
+
pass
|
|
704
|
+
|
|
705
|
+
# Method 3: Fallback - Add file reference as comment
|
|
706
|
+
from ..core.models import Comment
|
|
707
|
+
|
|
708
|
+
comment_content = f"📎 File reference: {file_path.name}"
|
|
709
|
+
if description:
|
|
710
|
+
comment_content += f"\n\n{description}"
|
|
711
|
+
|
|
712
|
+
comment_obj = Comment(
|
|
713
|
+
ticket_id=ticket_id,
|
|
714
|
+
content=comment_content,
|
|
715
|
+
author="cli-user",
|
|
716
|
+
)
|
|
717
|
+
|
|
718
|
+
comment = await adapter_instance.add_comment(comment_obj)
|
|
719
|
+
return {
|
|
720
|
+
"status": "completed",
|
|
721
|
+
"comment": comment,
|
|
722
|
+
"method": "comment_reference",
|
|
723
|
+
"note": "Adapter doesn't support attachments - added file reference as comment",
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
# Validate file before attempting upload
|
|
727
|
+
if not file_path.exists():
|
|
728
|
+
console.print(f"[red]✗[/red] File not found: {file_path}")
|
|
729
|
+
raise typer.Exit(1) from None
|
|
730
|
+
|
|
731
|
+
if not file_path.is_file():
|
|
732
|
+
console.print(f"[red]✗[/red] Path is not a file: {file_path}")
|
|
733
|
+
raise typer.Exit(1) from None
|
|
734
|
+
|
|
735
|
+
# Display file info
|
|
736
|
+
file_size = file_path.stat().st_size
|
|
737
|
+
size_mb = file_size / (1024 * 1024)
|
|
738
|
+
console.print(f"\n[dim]Attaching file to ticket {ticket_id}...[/dim]")
|
|
739
|
+
console.print(f" File: {file_path.name} ({size_mb:.2f} MB)")
|
|
740
|
+
|
|
741
|
+
# Detect MIME type
|
|
742
|
+
import mimetypes
|
|
743
|
+
|
|
744
|
+
mime_type, _ = mimetypes.guess_type(str(file_path))
|
|
745
|
+
if mime_type:
|
|
746
|
+
console.print(f" Type: {mime_type}")
|
|
747
|
+
|
|
748
|
+
try:
|
|
749
|
+
result = asyncio.run(_attach())
|
|
750
|
+
|
|
751
|
+
if result["status"] == "completed":
|
|
752
|
+
console.print(
|
|
753
|
+
f"\n[green]✓[/green] File attached successfully to {ticket_id}"
|
|
754
|
+
)
|
|
755
|
+
|
|
756
|
+
# Display attachment details based on method used
|
|
757
|
+
method = result.get("method", "unknown")
|
|
758
|
+
|
|
759
|
+
if method == "linear_native_upload":
|
|
760
|
+
console.print(" Method: Linear native upload")
|
|
761
|
+
if "file_url" in result:
|
|
762
|
+
console.print(f" URL: {result['file_url']}")
|
|
763
|
+
if "attachment" in result and isinstance(result["attachment"], dict):
|
|
764
|
+
att = result["attachment"]
|
|
765
|
+
if "id" in att:
|
|
766
|
+
console.print(f" ID: {att['id']}")
|
|
767
|
+
if "title" in att:
|
|
768
|
+
console.print(f" Title: {att['title']}")
|
|
769
|
+
|
|
770
|
+
elif method == "adapter_native":
|
|
771
|
+
console.print(" Method: Adapter native")
|
|
772
|
+
if "attachment" in result:
|
|
773
|
+
att = result["attachment"]
|
|
774
|
+
if isinstance(att, dict):
|
|
775
|
+
if "id" in att:
|
|
776
|
+
console.print(f" ID: {att['id']}")
|
|
777
|
+
if "url" in att:
|
|
778
|
+
console.print(f" URL: {att['url']}")
|
|
779
|
+
|
|
780
|
+
elif method == "comment_reference":
|
|
781
|
+
console.print(" Method: Comment reference")
|
|
782
|
+
console.print(f" [dim]{result.get('note', '')}[/dim]")
|
|
783
|
+
if "comment" in result:
|
|
784
|
+
comment = result["comment"]
|
|
785
|
+
if isinstance(comment, dict) and "id" in comment:
|
|
786
|
+
console.print(f" Comment ID: {comment['id']}")
|
|
787
|
+
|
|
788
|
+
else:
|
|
789
|
+
# Error case
|
|
790
|
+
error_msg = result.get("error", "Unknown error")
|
|
791
|
+
console.print(f"\n[red]✗[/red] Failed to attach file: {error_msg}")
|
|
792
|
+
raise typer.Exit(1) from None
|
|
793
|
+
|
|
794
|
+
except Exception as e:
|
|
795
|
+
console.print(f"\n[red]✗[/red] Failed to attach file: {e}")
|
|
796
|
+
raise typer.Exit(1) from None
|
|
797
|
+
|
|
798
|
+
|
|
559
799
|
@app.command()
|
|
560
800
|
def update(
|
|
561
801
|
ticket_id: str = typer.Argument(..., help="Ticket ID"),
|
mcp_ticketer/core/__init__.py
CHANGED
|
@@ -2,20 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
from .adapter import BaseAdapter
|
|
4
4
|
from .instructions import (
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
InstructionsError,
|
|
6
|
+
InstructionsNotFoundError,
|
|
7
|
+
InstructionsValidationError,
|
|
8
|
+
TicketInstructionsManager,
|
|
9
|
+
get_instructions,
|
|
10
|
+
)
|
|
11
|
+
from .models import (
|
|
12
|
+
Attachment,
|
|
13
|
+
Comment,
|
|
14
|
+
Epic,
|
|
15
|
+
Priority,
|
|
16
|
+
ProjectUpdate,
|
|
17
|
+
ProjectUpdateHealth,
|
|
18
|
+
Task,
|
|
19
|
+
TicketState,
|
|
20
|
+
TicketType,
|
|
10
21
|
)
|
|
11
|
-
from .models import Attachment, Comment, Epic, Priority, Task, TicketState, TicketType
|
|
12
22
|
from .registry import AdapterRegistry
|
|
23
|
+
from .state_matcher import (
|
|
24
|
+
SemanticStateMatcher,
|
|
25
|
+
StateMatchResult,
|
|
26
|
+
ValidationResult,
|
|
27
|
+
get_state_matcher,
|
|
28
|
+
)
|
|
13
29
|
|
|
14
30
|
__all__ = [
|
|
15
31
|
"Epic",
|
|
16
32
|
"Task",
|
|
17
33
|
"Comment",
|
|
18
34
|
"Attachment",
|
|
35
|
+
"ProjectUpdate",
|
|
36
|
+
"ProjectUpdateHealth",
|
|
19
37
|
"TicketState",
|
|
20
38
|
"Priority",
|
|
21
39
|
"TicketType",
|
|
@@ -26,4 +44,8 @@ __all__ = [
|
|
|
26
44
|
"InstructionsNotFoundError",
|
|
27
45
|
"InstructionsValidationError",
|
|
28
46
|
"get_instructions",
|
|
47
|
+
"SemanticStateMatcher",
|
|
48
|
+
"StateMatchResult",
|
|
49
|
+
"ValidationResult",
|
|
50
|
+
"get_state_matcher",
|
|
29
51
|
]
|