mcp-ticketer 0.12.0__py3-none-any.whl → 2.2.13__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/_version_scm.py +1 -0
- mcp_ticketer/adapters/aitrackdown.py +507 -6
- mcp_ticketer/adapters/asana/adapter.py +229 -0
- mcp_ticketer/adapters/asana/mappers.py +14 -0
- mcp_ticketer/adapters/github/__init__.py +26 -0
- mcp_ticketer/adapters/github/adapter.py +3229 -0
- mcp_ticketer/adapters/github/client.py +335 -0
- mcp_ticketer/adapters/github/mappers.py +797 -0
- mcp_ticketer/adapters/github/queries.py +692 -0
- mcp_ticketer/adapters/github/types.py +460 -0
- mcp_ticketer/adapters/hybrid.py +47 -5
- mcp_ticketer/adapters/jira/__init__.py +35 -0
- mcp_ticketer/adapters/jira/adapter.py +1351 -0
- mcp_ticketer/adapters/jira/client.py +271 -0
- mcp_ticketer/adapters/jira/mappers.py +246 -0
- mcp_ticketer/adapters/jira/queries.py +216 -0
- mcp_ticketer/adapters/jira/types.py +304 -0
- mcp_ticketer/adapters/linear/adapter.py +2730 -139
- mcp_ticketer/adapters/linear/client.py +175 -3
- mcp_ticketer/adapters/linear/mappers.py +203 -8
- mcp_ticketer/adapters/linear/queries.py +280 -3
- mcp_ticketer/adapters/linear/types.py +120 -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 +1288 -105
- 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/install_mcp_server.py +418 -0
- mcp_ticketer/cli/instruction_commands.py +6 -0
- mcp_ticketer/cli/main.py +267 -3175
- mcp_ticketer/cli/mcp_configure.py +821 -119
- mcp_ticketer/cli/mcp_server_commands.py +415 -0
- mcp_ticketer/cli/platform_detection.py +77 -12
- mcp_ticketer/cli/platform_installer.py +545 -0
- mcp_ticketer/cli/project_update_commands.py +350 -0
- mcp_ticketer/cli/setup_command.py +795 -0
- mcp_ticketer/cli/simple_health.py +12 -10
- mcp_ticketer/cli/ticket_commands.py +705 -103
- mcp_ticketer/cli/utils.py +113 -0
- mcp_ticketer/core/__init__.py +56 -6
- mcp_ticketer/core/adapter.py +533 -2
- 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/milestone_manager.py +252 -0
- mcp_ticketer/core/models.py +480 -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/project_utils.py +281 -0
- mcp_ticketer/core/project_validator.py +376 -0
- mcp_ticketer/core/session_state.py +176 -0
- mcp_ticketer/core/state_matcher.py +625 -0
- mcp_ticketer/core/url_parser.py +425 -0
- mcp_ticketer/core/validators.py +69 -0
- mcp_ticketer/mcp/server/__main__.py +2 -1
- mcp_ticketer/mcp/server/diagnostic_helper.py +175 -0
- mcp_ticketer/mcp/server/main.py +106 -25
- mcp_ticketer/mcp/server/routing.py +723 -0
- mcp_ticketer/mcp/server/server_sdk.py +58 -0
- mcp_ticketer/mcp/server/tools/__init__.py +33 -11
- mcp_ticketer/mcp/server/tools/analysis_tools.py +854 -0
- mcp_ticketer/mcp/server/tools/attachment_tools.py +5 -5
- 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 +1391 -145
- 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/milestone_tools.py +338 -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 +209 -97
- mcp_ticketer/mcp/server/tools/session_tools.py +308 -0
- mcp_ticketer/mcp/server/tools/ticket_tools.py +1107 -124
- mcp_ticketer/mcp/server/tools/user_ticket_tools.py +218 -236
- mcp_ticketer/queue/queue.py +68 -0
- 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.2.13.dist-info/METADATA +1396 -0
- mcp_ticketer-2.2.13.dist-info/RECORD +158 -0
- mcp_ticketer-2.2.13.dist-info/top_level.txt +2 -0
- py_mcp_installer/examples/phase3_demo.py +178 -0
- py_mcp_installer/scripts/manage_version.py +54 -0
- py_mcp_installer/setup.py +6 -0
- py_mcp_installer/src/py_mcp_installer/__init__.py +153 -0
- py_mcp_installer/src/py_mcp_installer/command_builder.py +445 -0
- py_mcp_installer/src/py_mcp_installer/config_manager.py +541 -0
- py_mcp_installer/src/py_mcp_installer/exceptions.py +243 -0
- py_mcp_installer/src/py_mcp_installer/installation_strategy.py +617 -0
- py_mcp_installer/src/py_mcp_installer/installer.py +656 -0
- py_mcp_installer/src/py_mcp_installer/mcp_inspector.py +750 -0
- py_mcp_installer/src/py_mcp_installer/platform_detector.py +451 -0
- py_mcp_installer/src/py_mcp_installer/platforms/__init__.py +26 -0
- py_mcp_installer/src/py_mcp_installer/platforms/claude_code.py +225 -0
- py_mcp_installer/src/py_mcp_installer/platforms/codex.py +181 -0
- py_mcp_installer/src/py_mcp_installer/platforms/cursor.py +191 -0
- py_mcp_installer/src/py_mcp_installer/types.py +222 -0
- py_mcp_installer/src/py_mcp_installer/utils.py +463 -0
- py_mcp_installer/tests/__init__.py +0 -0
- py_mcp_installer/tests/platforms/__init__.py +0 -0
- py_mcp_installer/tests/test_platform_detector.py +17 -0
- mcp_ticketer/adapters/github.py +0 -1574
- mcp_ticketer/adapters/jira.py +0 -1258
- 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/top_level.txt +0 -1
- {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.2.13.dist-info}/WHEEL +0 -0
- {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.2.13.dist-info}/entry_points.txt +0 -0
- {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.2.13.dist-info}/licenses/LICENSE +0 -0
mcp_ticketer/core/models.py
CHANGED
|
@@ -160,6 +160,37 @@ class TicketState(str, Enum):
|
|
|
160
160
|
"""
|
|
161
161
|
return target.value in self.valid_transitions().get(self, [])
|
|
162
162
|
|
|
163
|
+
def completion_level(self) -> int:
|
|
164
|
+
"""Get numeric completion level for state ordering.
|
|
165
|
+
|
|
166
|
+
Higher numbers indicate more complete states. Used for parent/child
|
|
167
|
+
state constraints where parents must be at least as complete as
|
|
168
|
+
their most complete child.
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
Completion level (0-7)
|
|
172
|
+
|
|
173
|
+
Example:
|
|
174
|
+
>>> TicketState.OPEN.completion_level()
|
|
175
|
+
0
|
|
176
|
+
>>> TicketState.DONE.completion_level()
|
|
177
|
+
6
|
|
178
|
+
>>> TicketState.DONE.completion_level() > TicketState.IN_PROGRESS.completion_level()
|
|
179
|
+
True
|
|
180
|
+
|
|
181
|
+
"""
|
|
182
|
+
levels = {
|
|
183
|
+
TicketState.OPEN: 0, # Not started
|
|
184
|
+
TicketState.BLOCKED: 1, # Blocked
|
|
185
|
+
TicketState.WAITING: 2, # Waiting
|
|
186
|
+
TicketState.IN_PROGRESS: 3, # In progress
|
|
187
|
+
TicketState.READY: 4, # Ready for review
|
|
188
|
+
TicketState.TESTED: 5, # Tested
|
|
189
|
+
TicketState.DONE: 6, # Done
|
|
190
|
+
TicketState.CLOSED: 7, # Closed (terminal)
|
|
191
|
+
}
|
|
192
|
+
return levels.get(self, 0)
|
|
193
|
+
|
|
163
194
|
|
|
164
195
|
class BaseTicket(BaseModel):
|
|
165
196
|
"""Base model for all ticket types with universal field mapping.
|
|
@@ -385,6 +416,454 @@ class Attachment(BaseModel):
|
|
|
385
416
|
return f"Attachment({self.filename}{size_str})"
|
|
386
417
|
|
|
387
418
|
|
|
419
|
+
class ProjectUpdateHealth(str, Enum):
|
|
420
|
+
"""Project health status indicator for status updates.
|
|
421
|
+
|
|
422
|
+
Represents the health/status of a project at the time of an update.
|
|
423
|
+
These states map to different platform-specific health indicators:
|
|
424
|
+
|
|
425
|
+
Platform Mappings:
|
|
426
|
+
- Linear: on_track, at_risk, off_track (1:1 mapping)
|
|
427
|
+
- GitHub V2: Uses ProjectV2StatusOptionConfiguration
|
|
428
|
+
- complete: Project is finished
|
|
429
|
+
- inactive: Project is not actively being worked on
|
|
430
|
+
- Asana: On Track, At Risk, Off Track (1:1 mapping)
|
|
431
|
+
- JIRA: Not directly supported (workaround via status comments)
|
|
432
|
+
|
|
433
|
+
Attributes:
|
|
434
|
+
ON_TRACK: Project is progressing as planned
|
|
435
|
+
AT_RISK: Project has some issues but recoverable
|
|
436
|
+
OFF_TRACK: Project is significantly behind or blocked
|
|
437
|
+
COMPLETE: Project is finished (GitHub-specific)
|
|
438
|
+
INACTIVE: Project is not actively being worked on (GitHub-specific)
|
|
439
|
+
|
|
440
|
+
Note:
|
|
441
|
+
Related to ticket 1M-238: Add project updates support with flexible
|
|
442
|
+
project identification.
|
|
443
|
+
|
|
444
|
+
"""
|
|
445
|
+
|
|
446
|
+
ON_TRACK = "on_track" # Linear, Asana
|
|
447
|
+
AT_RISK = "at_risk" # Linear, Asana
|
|
448
|
+
OFF_TRACK = "off_track" # Linear, Asana
|
|
449
|
+
COMPLETE = "complete" # GitHub only
|
|
450
|
+
INACTIVE = "inactive" # GitHub only
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
class ProjectUpdate(BaseModel):
|
|
454
|
+
"""Represents a project status update across different platforms.
|
|
455
|
+
|
|
456
|
+
ProjectUpdate provides a unified interface for creating and retrieving
|
|
457
|
+
project status updates with health indicators, supporting Linear, GitHub V2,
|
|
458
|
+
Asana, and JIRA (via workaround).
|
|
459
|
+
|
|
460
|
+
Platform Mappings:
|
|
461
|
+
- Linear: ProjectUpdate entity with health, diff_markdown, staleness
|
|
462
|
+
- GitHub V2: ProjectV2StatusUpdate with status options
|
|
463
|
+
- Asana: Project Status Updates with color-coded health
|
|
464
|
+
- JIRA: Comments with custom formatting (workaround)
|
|
465
|
+
|
|
466
|
+
The model includes platform-specific optional fields to support features
|
|
467
|
+
like Linear's auto-generated diffs and staleness indicators.
|
|
468
|
+
|
|
469
|
+
Attributes:
|
|
470
|
+
id: Unique identifier for the update
|
|
471
|
+
project_id: ID of the project this update belongs to
|
|
472
|
+
project_name: Optional human-readable project name
|
|
473
|
+
body: Markdown-formatted update content (required)
|
|
474
|
+
health: Optional health status indicator
|
|
475
|
+
created_at: Timestamp when update was created
|
|
476
|
+
updated_at: Timestamp when update was last modified
|
|
477
|
+
author_id: Optional ID of the user who created the update
|
|
478
|
+
author_name: Optional human-readable author name
|
|
479
|
+
url: Optional direct URL to the update
|
|
480
|
+
diff_markdown: Linear-specific auto-generated diff of project changes
|
|
481
|
+
is_stale: Linear-specific indicator if update is outdated
|
|
482
|
+
|
|
483
|
+
Example:
|
|
484
|
+
>>> update = ProjectUpdate(
|
|
485
|
+
... project_id="PROJ-123",
|
|
486
|
+
... body="Sprint completed with 15/20 stories done",
|
|
487
|
+
... health=ProjectUpdateHealth.AT_RISK,
|
|
488
|
+
... created_at=datetime.now()
|
|
489
|
+
... )
|
|
490
|
+
>>> print(update.model_dump_json())
|
|
491
|
+
|
|
492
|
+
Note:
|
|
493
|
+
Related to ticket 1M-238: Add project updates support with flexible
|
|
494
|
+
project identification.
|
|
495
|
+
|
|
496
|
+
"""
|
|
497
|
+
|
|
498
|
+
model_config = ConfigDict(use_enum_values=True)
|
|
499
|
+
|
|
500
|
+
id: str = Field(..., description="Unique update identifier")
|
|
501
|
+
project_id: str = Field(..., description="Parent project identifier")
|
|
502
|
+
project_name: str | None = Field(None, description="Human-readable project name")
|
|
503
|
+
body: str = Field(..., min_length=1, description="Markdown update content")
|
|
504
|
+
health: ProjectUpdateHealth | None = Field(
|
|
505
|
+
None, description="Project health status"
|
|
506
|
+
)
|
|
507
|
+
created_at: datetime = Field(..., description="Creation timestamp")
|
|
508
|
+
updated_at: datetime | None = Field(None, description="Last update timestamp")
|
|
509
|
+
author_id: str | None = Field(None, description="Update author identifier")
|
|
510
|
+
author_name: str | None = Field(None, description="Update author name")
|
|
511
|
+
url: str | None = Field(None, description="Direct URL to update")
|
|
512
|
+
|
|
513
|
+
# Platform-specific fields
|
|
514
|
+
diff_markdown: str | None = Field(
|
|
515
|
+
None, description="Linear: Auto-generated diff of project changes"
|
|
516
|
+
)
|
|
517
|
+
is_stale: bool | None = Field(
|
|
518
|
+
None, description="Linear: Indicator if update is outdated"
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
class Milestone(BaseModel):
|
|
523
|
+
"""Universal milestone model for cross-platform support.
|
|
524
|
+
|
|
525
|
+
A milestone is a collection of issues grouped by labels with a target date.
|
|
526
|
+
Progress is calculated by counting closed vs total issues matching the labels.
|
|
527
|
+
|
|
528
|
+
Platform Mappings:
|
|
529
|
+
- Linear: Milestones (with labels and target dates)
|
|
530
|
+
- GitHub: Milestones (native support with due dates)
|
|
531
|
+
- JIRA: Versions/Releases (with target dates)
|
|
532
|
+
- Asana: Projects with dates (workaround via filtering)
|
|
533
|
+
|
|
534
|
+
The model follows the user's definition: "A milestone is a list of labels
|
|
535
|
+
with target dates, into which issues can be grouped."
|
|
536
|
+
|
|
537
|
+
Attributes:
|
|
538
|
+
id: Unique milestone identifier
|
|
539
|
+
name: Milestone name
|
|
540
|
+
target_date: Target completion date (ISO format: YYYY-MM-DD)
|
|
541
|
+
state: Milestone state (open, active, completed, closed)
|
|
542
|
+
description: Milestone description
|
|
543
|
+
labels: Labels that define this milestone's scope
|
|
544
|
+
total_issues: Total issues in milestone (calculated)
|
|
545
|
+
closed_issues: Closed issues in milestone (calculated)
|
|
546
|
+
progress_pct: Progress percentage 0-100 (calculated)
|
|
547
|
+
project_id: Associated project/epic ID
|
|
548
|
+
created_at: Creation timestamp
|
|
549
|
+
updated_at: Last update timestamp
|
|
550
|
+
platform_data: Platform-specific metadata
|
|
551
|
+
|
|
552
|
+
Example:
|
|
553
|
+
>>> milestone = Milestone(
|
|
554
|
+
... name="v2.1.0 Release",
|
|
555
|
+
... target_date=date(2025, 12, 31),
|
|
556
|
+
... labels=["v2.1", "release"],
|
|
557
|
+
... project_id="proj-123"
|
|
558
|
+
... )
|
|
559
|
+
>>> milestone.total_issues = 15
|
|
560
|
+
>>> milestone.closed_issues = 8
|
|
561
|
+
>>> milestone.progress_pct = 53.3
|
|
562
|
+
|
|
563
|
+
Note:
|
|
564
|
+
Related to ticket 1M-607: Add milestone support (Phase 1 - Core Infrastructure)
|
|
565
|
+
|
|
566
|
+
"""
|
|
567
|
+
|
|
568
|
+
model_config = ConfigDict(use_enum_values=True)
|
|
569
|
+
|
|
570
|
+
id: str | None = Field(None, description="Unique milestone identifier")
|
|
571
|
+
name: str = Field(..., min_length=1, description="Milestone name")
|
|
572
|
+
target_date: datetime | None = Field(
|
|
573
|
+
None, description="Target completion date (ISO format: YYYY-MM-DD)"
|
|
574
|
+
)
|
|
575
|
+
state: str = Field(
|
|
576
|
+
"open", description="Milestone state: open, active, completed, closed"
|
|
577
|
+
)
|
|
578
|
+
description: str = Field("", description="Milestone description")
|
|
579
|
+
|
|
580
|
+
# Label-based grouping (user's definition)
|
|
581
|
+
labels: list[str] = Field(
|
|
582
|
+
default_factory=list, description="Labels that define this milestone"
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
# Progress tracking (calculated fields)
|
|
586
|
+
total_issues: int = Field(0, ge=0, description="Total issues in milestone")
|
|
587
|
+
closed_issues: int = Field(0, ge=0, description="Closed issues in milestone")
|
|
588
|
+
progress_pct: float = Field(
|
|
589
|
+
0.0, ge=0.0, le=100.0, description="Progress percentage (0-100)"
|
|
590
|
+
)
|
|
591
|
+
|
|
592
|
+
# Metadata
|
|
593
|
+
project_id: str | None = Field(None, description="Associated project ID")
|
|
594
|
+
created_at: datetime | None = Field(None, description="Creation timestamp")
|
|
595
|
+
updated_at: datetime | None = Field(None, description="Last update timestamp")
|
|
596
|
+
|
|
597
|
+
# Platform-specific data
|
|
598
|
+
platform_data: dict[str, Any] = Field(
|
|
599
|
+
default_factory=dict, description="Platform-specific metadata"
|
|
600
|
+
)
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
class ProjectState(str, Enum):
|
|
604
|
+
"""Project state across platforms.
|
|
605
|
+
|
|
606
|
+
Maps to different platform concepts:
|
|
607
|
+
- Linear: planned, started, completed, paused, canceled
|
|
608
|
+
- GitHub V2: OPEN, CLOSED (with status field for more granular states)
|
|
609
|
+
- JIRA: Not directly supported (use project status or custom fields)
|
|
610
|
+
|
|
611
|
+
Attributes:
|
|
612
|
+
PLANNED: Project is planned but not yet started
|
|
613
|
+
ACTIVE: Project is actively being worked on
|
|
614
|
+
COMPLETED: Project is finished successfully
|
|
615
|
+
ARCHIVED: Project is archived (no longer active)
|
|
616
|
+
CANCELLED: Project was cancelled before completion
|
|
617
|
+
|
|
618
|
+
"""
|
|
619
|
+
|
|
620
|
+
PLANNED = "planned"
|
|
621
|
+
ACTIVE = "active"
|
|
622
|
+
COMPLETED = "completed"
|
|
623
|
+
ARCHIVED = "archived"
|
|
624
|
+
CANCELLED = "cancelled"
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
class ProjectVisibility(str, Enum):
|
|
628
|
+
"""Project visibility setting.
|
|
629
|
+
|
|
630
|
+
Controls who can view the project across platforms.
|
|
631
|
+
|
|
632
|
+
Attributes:
|
|
633
|
+
PUBLIC: Visible to everyone
|
|
634
|
+
PRIVATE: Visible only to members
|
|
635
|
+
TEAM: Visible to team members
|
|
636
|
+
|
|
637
|
+
"""
|
|
638
|
+
|
|
639
|
+
PUBLIC = "public"
|
|
640
|
+
PRIVATE = "private"
|
|
641
|
+
TEAM = "team"
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
class ProjectScope(str, Enum):
|
|
645
|
+
"""Project organizational scope.
|
|
646
|
+
|
|
647
|
+
Defines the level at which a project exists in the organization hierarchy.
|
|
648
|
+
|
|
649
|
+
Platform Mappings:
|
|
650
|
+
- Linear: TEAM (projects belong to teams) or ORGANIZATION
|
|
651
|
+
- GitHub: REPOSITORY, USER, or ORGANIZATION
|
|
652
|
+
- JIRA: PROJECT (inherent) or ORGANIZATION (via project hierarchy)
|
|
653
|
+
|
|
654
|
+
Attributes:
|
|
655
|
+
USER: User-level project (GitHub Projects V2)
|
|
656
|
+
TEAM: Team-level project (Linear, GitHub org teams)
|
|
657
|
+
ORGANIZATION: Organization-level project (cross-team)
|
|
658
|
+
REPOSITORY: Repository-scoped project (GitHub)
|
|
659
|
+
|
|
660
|
+
"""
|
|
661
|
+
|
|
662
|
+
USER = "user"
|
|
663
|
+
TEAM = "team"
|
|
664
|
+
ORGANIZATION = "organization"
|
|
665
|
+
REPOSITORY = "repository"
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
class Project(BaseModel):
|
|
669
|
+
"""Unified project model across platforms.
|
|
670
|
+
|
|
671
|
+
Projects represent strategic-level containers for issues, superseding the
|
|
672
|
+
Epic model with a more comprehensive structure that maps cleanly to:
|
|
673
|
+
- Linear Projects
|
|
674
|
+
- GitHub Projects V2
|
|
675
|
+
- JIRA Projects/Epics
|
|
676
|
+
|
|
677
|
+
This model provides backward compatibility through conversion utilities
|
|
678
|
+
(see project_utils.py) while enabling richer project management features.
|
|
679
|
+
|
|
680
|
+
Attributes:
|
|
681
|
+
id: Unique identifier in MCP Ticketer namespace
|
|
682
|
+
platform: Platform identifier ("linear", "github", "jira")
|
|
683
|
+
platform_id: Original platform-specific identifier
|
|
684
|
+
scope: Organizational scope of the project
|
|
685
|
+
name: Project name (required)
|
|
686
|
+
description: Detailed project description
|
|
687
|
+
state: Current project state
|
|
688
|
+
visibility: Who can view the project
|
|
689
|
+
url: Direct URL to project in platform
|
|
690
|
+
created_at: When project was created
|
|
691
|
+
updated_at: When project was last modified
|
|
692
|
+
start_date: Planned or actual start date
|
|
693
|
+
target_date: Target completion date
|
|
694
|
+
completed_at: Actual completion date
|
|
695
|
+
owner_id: Project owner/lead user ID
|
|
696
|
+
owner_name: Project owner/lead display name
|
|
697
|
+
team_id: Team this project belongs to
|
|
698
|
+
team_name: Team display name
|
|
699
|
+
child_issues: List of issue IDs in this project
|
|
700
|
+
issue_count: Total number of issues
|
|
701
|
+
completed_count: Number of completed issues
|
|
702
|
+
in_progress_count: Number of in-progress issues
|
|
703
|
+
progress_percentage: Overall completion percentage
|
|
704
|
+
extra_data: Platform-specific additional data
|
|
705
|
+
|
|
706
|
+
Example:
|
|
707
|
+
>>> project = Project(
|
|
708
|
+
... id="proj-123",
|
|
709
|
+
... platform="linear",
|
|
710
|
+
... platform_id="eac28953c267",
|
|
711
|
+
... scope=ProjectScope.TEAM,
|
|
712
|
+
... name="MCP Ticketer v2.0",
|
|
713
|
+
... state=ProjectState.ACTIVE,
|
|
714
|
+
... visibility=ProjectVisibility.TEAM
|
|
715
|
+
... )
|
|
716
|
+
|
|
717
|
+
"""
|
|
718
|
+
|
|
719
|
+
model_config = ConfigDict(use_enum_values=True)
|
|
720
|
+
|
|
721
|
+
# Core identification
|
|
722
|
+
id: str = Field(..., description="Unique identifier")
|
|
723
|
+
platform: str = Field(..., description="Platform name (linear, github, jira)")
|
|
724
|
+
platform_id: str = Field(..., description="Original platform ID")
|
|
725
|
+
scope: ProjectScope = Field(..., description="Organizational scope")
|
|
726
|
+
|
|
727
|
+
# Basic information
|
|
728
|
+
name: str = Field(..., min_length=1, description="Project name")
|
|
729
|
+
description: str | None = Field(None, description="Project description")
|
|
730
|
+
state: ProjectState = Field(ProjectState.PLANNED, description="Current state")
|
|
731
|
+
visibility: ProjectVisibility = Field(
|
|
732
|
+
ProjectVisibility.TEAM, description="Visibility"
|
|
733
|
+
)
|
|
734
|
+
|
|
735
|
+
# URLs and references
|
|
736
|
+
url: str | None = Field(None, description="Direct URL to project")
|
|
737
|
+
|
|
738
|
+
# Dates
|
|
739
|
+
created_at: datetime | None = Field(None, description="Creation timestamp")
|
|
740
|
+
updated_at: datetime | None = Field(None, description="Last update timestamp")
|
|
741
|
+
start_date: datetime | None = Field(None, description="Start date")
|
|
742
|
+
target_date: datetime | None = Field(None, description="Target completion date")
|
|
743
|
+
completed_at: datetime | None = Field(None, description="Completion timestamp")
|
|
744
|
+
|
|
745
|
+
# Ownership
|
|
746
|
+
owner_id: str | None = Field(None, description="Owner user ID")
|
|
747
|
+
owner_name: str | None = Field(None, description="Owner display name")
|
|
748
|
+
team_id: str | None = Field(None, description="Team ID")
|
|
749
|
+
team_name: str | None = Field(None, description="Team display name")
|
|
750
|
+
|
|
751
|
+
# Issue relationships
|
|
752
|
+
child_issues: list[str] = Field(default_factory=list, description="Child issue IDs")
|
|
753
|
+
issue_count: int | None = Field(None, ge=0, description="Total issue count")
|
|
754
|
+
completed_count: int | None = Field(None, ge=0, description="Completed issues")
|
|
755
|
+
in_progress_count: int | None = Field(None, ge=0, description="In-progress issues")
|
|
756
|
+
progress_percentage: float | None = Field(
|
|
757
|
+
None, ge=0.0, le=100.0, description="Completion percentage"
|
|
758
|
+
)
|
|
759
|
+
|
|
760
|
+
# Platform-specific data
|
|
761
|
+
extra_data: dict[str, Any] = Field(
|
|
762
|
+
default_factory=dict, description="Platform-specific metadata"
|
|
763
|
+
)
|
|
764
|
+
|
|
765
|
+
def calculate_progress(self) -> float:
|
|
766
|
+
"""Calculate progress percentage from issue counts.
|
|
767
|
+
|
|
768
|
+
Returns:
|
|
769
|
+
Progress percentage (0-100), or 0 if no issues
|
|
770
|
+
|
|
771
|
+
"""
|
|
772
|
+
if not self.issue_count or self.issue_count == 0:
|
|
773
|
+
return 0.0
|
|
774
|
+
|
|
775
|
+
completed = self.completed_count or 0
|
|
776
|
+
return (completed / self.issue_count) * 100.0
|
|
777
|
+
|
|
778
|
+
|
|
779
|
+
class ProjectStatistics(BaseModel):
|
|
780
|
+
"""Statistics and metrics for a project.
|
|
781
|
+
|
|
782
|
+
Provides calculated metrics for project health and progress tracking.
|
|
783
|
+
These statistics are typically computed from current project state
|
|
784
|
+
rather than stored directly.
|
|
785
|
+
|
|
786
|
+
Attributes:
|
|
787
|
+
project_id: ID of the project these stats belong to (optional for compatibility)
|
|
788
|
+
total_issues: Total number of issues (legacy field, use total_count)
|
|
789
|
+
completed_issues: Count of completed issues (legacy field, use completed_count)
|
|
790
|
+
in_progress_issues: Count of in-progress issues (legacy field, use in_progress_count)
|
|
791
|
+
open_issues: Count of open/backlog issues (legacy field, use open_count)
|
|
792
|
+
blocked_issues: Count of blocked issues (legacy field, use blocked_count)
|
|
793
|
+
total_count: Total number of issues (preferred)
|
|
794
|
+
open_count: Count of open issues (preferred)
|
|
795
|
+
in_progress_count: Count of in-progress issues (preferred)
|
|
796
|
+
completed_count: Count of completed issues (preferred)
|
|
797
|
+
blocked_count: Count of blocked issues (preferred)
|
|
798
|
+
priority_low_count: Count of low priority issues
|
|
799
|
+
priority_medium_count: Count of medium priority issues
|
|
800
|
+
priority_high_count: Count of high priority issues
|
|
801
|
+
priority_critical_count: Count of critical priority issues
|
|
802
|
+
health: Project health status (on_track, at_risk, off_track)
|
|
803
|
+
progress_percentage: Overall completion percentage
|
|
804
|
+
velocity: Issues completed per week (if available)
|
|
805
|
+
estimated_completion: Projected completion date
|
|
806
|
+
|
|
807
|
+
Example:
|
|
808
|
+
>>> stats = ProjectStatistics(
|
|
809
|
+
... total_count=50,
|
|
810
|
+
... completed_count=30,
|
|
811
|
+
... in_progress_count=15,
|
|
812
|
+
... open_count=5,
|
|
813
|
+
... blocked_count=0,
|
|
814
|
+
... priority_high_count=10,
|
|
815
|
+
... health="on_track",
|
|
816
|
+
... progress_percentage=60.0
|
|
817
|
+
... )
|
|
818
|
+
|
|
819
|
+
"""
|
|
820
|
+
|
|
821
|
+
model_config = ConfigDict(use_enum_values=True)
|
|
822
|
+
|
|
823
|
+
# Legacy fields for backward compatibility (optional)
|
|
824
|
+
project_id: str | None = Field(None, description="Project identifier (legacy)")
|
|
825
|
+
total_issues: int | None = Field(
|
|
826
|
+
None, ge=0, description="Total issue count (legacy)"
|
|
827
|
+
)
|
|
828
|
+
completed_issues: int | None = Field(
|
|
829
|
+
None, ge=0, description="Completed issues (legacy)"
|
|
830
|
+
)
|
|
831
|
+
in_progress_issues: int | None = Field(
|
|
832
|
+
None, ge=0, description="In-progress issues (legacy)"
|
|
833
|
+
)
|
|
834
|
+
open_issues: int | None = Field(
|
|
835
|
+
None, ge=0, description="Open/backlog issues (legacy)"
|
|
836
|
+
)
|
|
837
|
+
blocked_issues: int | None = Field(
|
|
838
|
+
None, ge=0, description="Blocked issues (legacy)"
|
|
839
|
+
)
|
|
840
|
+
|
|
841
|
+
# New preferred fields
|
|
842
|
+
total_count: int = Field(0, ge=0, description="Total issue count")
|
|
843
|
+
open_count: int = Field(0, ge=0, description="Open issues")
|
|
844
|
+
in_progress_count: int = Field(0, ge=0, description="In-progress issues")
|
|
845
|
+
completed_count: int = Field(0, ge=0, description="Completed issues")
|
|
846
|
+
blocked_count: int = Field(0, ge=0, description="Blocked issues")
|
|
847
|
+
|
|
848
|
+
# Priority distribution
|
|
849
|
+
priority_low_count: int = Field(0, ge=0, description="Low priority issues")
|
|
850
|
+
priority_medium_count: int = Field(0, ge=0, description="Medium priority issues")
|
|
851
|
+
priority_high_count: int = Field(0, ge=0, description="High priority issues")
|
|
852
|
+
priority_critical_count: int = Field(
|
|
853
|
+
0, ge=0, description="Critical priority issues"
|
|
854
|
+
)
|
|
855
|
+
|
|
856
|
+
# Health and progress
|
|
857
|
+
health: str = Field(
|
|
858
|
+
"on_track", description="Health status: on_track, at_risk, off_track"
|
|
859
|
+
)
|
|
860
|
+
progress_percentage: float = Field(0.0, ge=0.0, le=100.0, description="Progress %")
|
|
861
|
+
velocity: float | None = Field(None, description="Issues/week completion rate")
|
|
862
|
+
estimated_completion: datetime | None = Field(
|
|
863
|
+
None, description="Projected completion date"
|
|
864
|
+
)
|
|
865
|
+
|
|
866
|
+
|
|
388
867
|
class SearchQuery(BaseModel):
|
|
389
868
|
"""Search query parameters."""
|
|
390
869
|
|
|
@@ -393,5 +872,6 @@ class SearchQuery(BaseModel):
|
|
|
393
872
|
priority: Priority | None = Field(None, description="Filter by priority")
|
|
394
873
|
tags: list[str] | None = Field(None, description="Filter by tags")
|
|
395
874
|
assignee: str | None = Field(None, description="Filter by assignee")
|
|
875
|
+
project: str | None = Field(None, description="Filter by project/epic ID or name")
|
|
396
876
|
limit: int = Field(10, gt=0, le=100, description="Maximum results")
|
|
397
877
|
offset: int = Field(0, ge=0, description="Result offset for pagination")
|