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.

Files changed (129) hide show
  1. mcp_ticketer/__init__.py +10 -10
  2. mcp_ticketer/__version__.py +1 -1
  3. mcp_ticketer/_version_scm.py +1 -0
  4. mcp_ticketer/adapters/aitrackdown.py +507 -6
  5. mcp_ticketer/adapters/asana/adapter.py +229 -0
  6. mcp_ticketer/adapters/asana/mappers.py +14 -0
  7. mcp_ticketer/adapters/github/__init__.py +26 -0
  8. mcp_ticketer/adapters/github/adapter.py +3229 -0
  9. mcp_ticketer/adapters/github/client.py +335 -0
  10. mcp_ticketer/adapters/github/mappers.py +797 -0
  11. mcp_ticketer/adapters/github/queries.py +692 -0
  12. mcp_ticketer/adapters/github/types.py +460 -0
  13. mcp_ticketer/adapters/hybrid.py +47 -5
  14. mcp_ticketer/adapters/jira/__init__.py +35 -0
  15. mcp_ticketer/adapters/jira/adapter.py +1351 -0
  16. mcp_ticketer/adapters/jira/client.py +271 -0
  17. mcp_ticketer/adapters/jira/mappers.py +246 -0
  18. mcp_ticketer/adapters/jira/queries.py +216 -0
  19. mcp_ticketer/adapters/jira/types.py +304 -0
  20. mcp_ticketer/adapters/linear/adapter.py +2730 -139
  21. mcp_ticketer/adapters/linear/client.py +175 -3
  22. mcp_ticketer/adapters/linear/mappers.py +203 -8
  23. mcp_ticketer/adapters/linear/queries.py +280 -3
  24. mcp_ticketer/adapters/linear/types.py +120 -4
  25. mcp_ticketer/analysis/__init__.py +56 -0
  26. mcp_ticketer/analysis/dependency_graph.py +255 -0
  27. mcp_ticketer/analysis/health_assessment.py +304 -0
  28. mcp_ticketer/analysis/orphaned.py +218 -0
  29. mcp_ticketer/analysis/project_status.py +594 -0
  30. mcp_ticketer/analysis/similarity.py +224 -0
  31. mcp_ticketer/analysis/staleness.py +266 -0
  32. mcp_ticketer/automation/__init__.py +11 -0
  33. mcp_ticketer/automation/project_updates.py +378 -0
  34. mcp_ticketer/cli/adapter_diagnostics.py +3 -1
  35. mcp_ticketer/cli/auggie_configure.py +17 -5
  36. mcp_ticketer/cli/codex_configure.py +97 -61
  37. mcp_ticketer/cli/configure.py +1288 -105
  38. mcp_ticketer/cli/cursor_configure.py +314 -0
  39. mcp_ticketer/cli/diagnostics.py +13 -12
  40. mcp_ticketer/cli/discover.py +5 -0
  41. mcp_ticketer/cli/gemini_configure.py +17 -5
  42. mcp_ticketer/cli/init_command.py +880 -0
  43. mcp_ticketer/cli/install_mcp_server.py +418 -0
  44. mcp_ticketer/cli/instruction_commands.py +6 -0
  45. mcp_ticketer/cli/main.py +267 -3175
  46. mcp_ticketer/cli/mcp_configure.py +821 -119
  47. mcp_ticketer/cli/mcp_server_commands.py +415 -0
  48. mcp_ticketer/cli/platform_detection.py +77 -12
  49. mcp_ticketer/cli/platform_installer.py +545 -0
  50. mcp_ticketer/cli/project_update_commands.py +350 -0
  51. mcp_ticketer/cli/setup_command.py +795 -0
  52. mcp_ticketer/cli/simple_health.py +12 -10
  53. mcp_ticketer/cli/ticket_commands.py +705 -103
  54. mcp_ticketer/cli/utils.py +113 -0
  55. mcp_ticketer/core/__init__.py +56 -6
  56. mcp_ticketer/core/adapter.py +533 -2
  57. mcp_ticketer/core/config.py +21 -21
  58. mcp_ticketer/core/exceptions.py +7 -1
  59. mcp_ticketer/core/label_manager.py +732 -0
  60. mcp_ticketer/core/mappers.py +31 -19
  61. mcp_ticketer/core/milestone_manager.py +252 -0
  62. mcp_ticketer/core/models.py +480 -0
  63. mcp_ticketer/core/onepassword_secrets.py +1 -1
  64. mcp_ticketer/core/priority_matcher.py +463 -0
  65. mcp_ticketer/core/project_config.py +132 -14
  66. mcp_ticketer/core/project_utils.py +281 -0
  67. mcp_ticketer/core/project_validator.py +376 -0
  68. mcp_ticketer/core/session_state.py +176 -0
  69. mcp_ticketer/core/state_matcher.py +625 -0
  70. mcp_ticketer/core/url_parser.py +425 -0
  71. mcp_ticketer/core/validators.py +69 -0
  72. mcp_ticketer/mcp/server/__main__.py +2 -1
  73. mcp_ticketer/mcp/server/diagnostic_helper.py +175 -0
  74. mcp_ticketer/mcp/server/main.py +106 -25
  75. mcp_ticketer/mcp/server/routing.py +723 -0
  76. mcp_ticketer/mcp/server/server_sdk.py +58 -0
  77. mcp_ticketer/mcp/server/tools/__init__.py +33 -11
  78. mcp_ticketer/mcp/server/tools/analysis_tools.py +854 -0
  79. mcp_ticketer/mcp/server/tools/attachment_tools.py +5 -5
  80. mcp_ticketer/mcp/server/tools/bulk_tools.py +259 -202
  81. mcp_ticketer/mcp/server/tools/comment_tools.py +74 -12
  82. mcp_ticketer/mcp/server/tools/config_tools.py +1391 -145
  83. mcp_ticketer/mcp/server/tools/diagnostic_tools.py +211 -0
  84. mcp_ticketer/mcp/server/tools/hierarchy_tools.py +870 -460
  85. mcp_ticketer/mcp/server/tools/instruction_tools.py +7 -5
  86. mcp_ticketer/mcp/server/tools/label_tools.py +942 -0
  87. mcp_ticketer/mcp/server/tools/milestone_tools.py +338 -0
  88. mcp_ticketer/mcp/server/tools/pr_tools.py +3 -7
  89. mcp_ticketer/mcp/server/tools/project_status_tools.py +158 -0
  90. mcp_ticketer/mcp/server/tools/project_update_tools.py +473 -0
  91. mcp_ticketer/mcp/server/tools/search_tools.py +209 -97
  92. mcp_ticketer/mcp/server/tools/session_tools.py +308 -0
  93. mcp_ticketer/mcp/server/tools/ticket_tools.py +1107 -124
  94. mcp_ticketer/mcp/server/tools/user_ticket_tools.py +218 -236
  95. mcp_ticketer/queue/queue.py +68 -0
  96. mcp_ticketer/queue/worker.py +1 -1
  97. mcp_ticketer/utils/__init__.py +5 -0
  98. mcp_ticketer/utils/token_utils.py +246 -0
  99. mcp_ticketer-2.2.13.dist-info/METADATA +1396 -0
  100. mcp_ticketer-2.2.13.dist-info/RECORD +158 -0
  101. mcp_ticketer-2.2.13.dist-info/top_level.txt +2 -0
  102. py_mcp_installer/examples/phase3_demo.py +178 -0
  103. py_mcp_installer/scripts/manage_version.py +54 -0
  104. py_mcp_installer/setup.py +6 -0
  105. py_mcp_installer/src/py_mcp_installer/__init__.py +153 -0
  106. py_mcp_installer/src/py_mcp_installer/command_builder.py +445 -0
  107. py_mcp_installer/src/py_mcp_installer/config_manager.py +541 -0
  108. py_mcp_installer/src/py_mcp_installer/exceptions.py +243 -0
  109. py_mcp_installer/src/py_mcp_installer/installation_strategy.py +617 -0
  110. py_mcp_installer/src/py_mcp_installer/installer.py +656 -0
  111. py_mcp_installer/src/py_mcp_installer/mcp_inspector.py +750 -0
  112. py_mcp_installer/src/py_mcp_installer/platform_detector.py +451 -0
  113. py_mcp_installer/src/py_mcp_installer/platforms/__init__.py +26 -0
  114. py_mcp_installer/src/py_mcp_installer/platforms/claude_code.py +225 -0
  115. py_mcp_installer/src/py_mcp_installer/platforms/codex.py +181 -0
  116. py_mcp_installer/src/py_mcp_installer/platforms/cursor.py +191 -0
  117. py_mcp_installer/src/py_mcp_installer/types.py +222 -0
  118. py_mcp_installer/src/py_mcp_installer/utils.py +463 -0
  119. py_mcp_installer/tests/__init__.py +0 -0
  120. py_mcp_installer/tests/platforms/__init__.py +0 -0
  121. py_mcp_installer/tests/test_platform_detector.py +17 -0
  122. mcp_ticketer/adapters/github.py +0 -1574
  123. mcp_ticketer/adapters/jira.py +0 -1258
  124. mcp_ticketer-0.12.0.dist-info/METADATA +0 -550
  125. mcp_ticketer-0.12.0.dist-info/RECORD +0 -91
  126. mcp_ticketer-0.12.0.dist-info/top_level.txt +0 -1
  127. {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.2.13.dist-info}/WHEEL +0 -0
  128. {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.2.13.dist-info}/entry_points.txt +0 -0
  129. {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.2.13.dist-info}/licenses/LICENSE +0 -0
@@ -351,13 +351,18 @@ SEARCH_ISSUE_BY_IDENTIFIER_QUERY = """
351
351
  """
352
352
 
353
353
  LIST_PROJECTS_QUERY = (
354
- PROJECT_FRAGMENT
354
+ TEAM_FRAGMENT # Required by PROJECT_FRAGMENT which uses ...TeamFields
355
+ + PROJECT_FRAGMENT
355
356
  + """
356
- query ListProjects($filter: ProjectFilter, $first: Int!) {
357
- projects(filter: $filter, first: $first, orderBy: updatedAt) {
357
+ query ListProjects($filter: ProjectFilter, $first: Int!, $after: String) {
358
+ projects(filter: $filter, first: $first, after: $after, orderBy: updatedAt) {
358
359
  nodes {
359
360
  ...ProjectFields
360
361
  }
362
+ pageInfo {
363
+ hasNextPage
364
+ endCursor
365
+ }
361
366
  }
362
367
  }
363
368
  """
@@ -387,3 +392,275 @@ GET_CURRENT_USER_QUERY = (
387
392
  }
388
393
  """
389
394
  )
395
+
396
+ CREATE_LABEL_MUTATION = """
397
+ mutation CreateLabel($input: IssueLabelCreateInput!) {
398
+ issueLabelCreate(input: $input) {
399
+ success
400
+ issueLabel {
401
+ id
402
+ name
403
+ color
404
+ description
405
+ }
406
+ }
407
+ }
408
+ """
409
+
410
+ LIST_CYCLES_QUERY = """
411
+ query GetCycles($teamId: String!, $first: Int!, $after: String) {
412
+ team(id: $teamId) {
413
+ cycles(first: $first, after: $after) {
414
+ nodes {
415
+ id
416
+ name
417
+ number
418
+ startsAt
419
+ endsAt
420
+ completedAt
421
+ progress
422
+ }
423
+ pageInfo {
424
+ hasNextPage
425
+ endCursor
426
+ }
427
+ }
428
+ }
429
+ }
430
+ """
431
+
432
+ GET_ISSUE_STATUS_QUERY = """
433
+ query GetIssueStatus($issueId: String!) {
434
+ issue(id: $issueId) {
435
+ id
436
+ state {
437
+ id
438
+ name
439
+ type
440
+ color
441
+ description
442
+ position
443
+ }
444
+ }
445
+ }
446
+ """
447
+
448
+ LIST_ISSUE_STATUSES_QUERY = """
449
+ query GetWorkflowStates($teamId: String!) {
450
+ team(id: $teamId) {
451
+ states {
452
+ nodes {
453
+ id
454
+ name
455
+ type
456
+ color
457
+ description
458
+ position
459
+ }
460
+ }
461
+ }
462
+ }
463
+ """
464
+
465
+ GET_CUSTOM_VIEW_QUERY = (
466
+ ISSUE_LIST_FRAGMENTS
467
+ + """
468
+ query GetCustomView($viewId: String!, $first: Int!) {
469
+ customView(id: $viewId) {
470
+ id
471
+ name
472
+ description
473
+ issues(first: $first) {
474
+ nodes {
475
+ ...IssueCompactFields
476
+ }
477
+ pageInfo {
478
+ hasNextPage
479
+ }
480
+ }
481
+ }
482
+ }
483
+ """
484
+ )
485
+
486
+ # Project Update queries and mutations (1M-238)
487
+
488
+ PROJECT_UPDATE_FRAGMENT = """
489
+ fragment ProjectUpdateFields on ProjectUpdate {
490
+ id
491
+ body
492
+ health
493
+ createdAt
494
+ updatedAt
495
+ diffMarkdown
496
+ url
497
+ user {
498
+ id
499
+ name
500
+ email
501
+ }
502
+ project {
503
+ id
504
+ name
505
+ slugId
506
+ }
507
+ }
508
+ """
509
+
510
+ CREATE_PROJECT_UPDATE_MUTATION = (
511
+ PROJECT_UPDATE_FRAGMENT
512
+ + """
513
+ mutation ProjectUpdateCreate(
514
+ $projectId: String!
515
+ $body: String!
516
+ $health: ProjectUpdateHealthType
517
+ ) {
518
+ projectUpdateCreate(
519
+ input: {
520
+ projectId: $projectId
521
+ body: $body
522
+ health: $health
523
+ }
524
+ ) {
525
+ success
526
+ projectUpdate {
527
+ ...ProjectUpdateFields
528
+ }
529
+ }
530
+ }
531
+ """
532
+ )
533
+
534
+ LIST_PROJECT_UPDATES_QUERY = (
535
+ PROJECT_UPDATE_FRAGMENT
536
+ + """
537
+ query ProjectUpdates($projectId: String!, $first: Int) {
538
+ project(id: $projectId) {
539
+ id
540
+ name
541
+ projectUpdates(first: $first) {
542
+ nodes {
543
+ ...ProjectUpdateFields
544
+ }
545
+ }
546
+ }
547
+ }
548
+ """
549
+ )
550
+
551
+ GET_PROJECT_UPDATE_QUERY = (
552
+ PROJECT_UPDATE_FRAGMENT
553
+ + """
554
+ query ProjectUpdate($id: String!) {
555
+ projectUpdate(id: $id) {
556
+ ...ProjectUpdateFields
557
+ }
558
+ }
559
+ """
560
+ )
561
+
562
+ # Milestone/Cycle Operations (1M-607 Phase 2)
563
+
564
+ CREATE_CYCLE_MUTATION = """
565
+ mutation CycleCreate($input: CycleCreateInput!) {
566
+ cycleCreate(input: $input) {
567
+ success
568
+ cycle {
569
+ id
570
+ name
571
+ description
572
+ startsAt
573
+ endsAt
574
+ completedAt
575
+ progress
576
+ completedIssueCount
577
+ issueCount
578
+ team {
579
+ id
580
+ name
581
+ }
582
+ }
583
+ }
584
+ }
585
+ """
586
+
587
+ GET_CYCLE_QUERY = """
588
+ query Cycle($id: String!) {
589
+ cycle(id: $id) {
590
+ id
591
+ name
592
+ description
593
+ startsAt
594
+ endsAt
595
+ completedAt
596
+ progress
597
+ completedIssueCount
598
+ issueCount
599
+ team {
600
+ id
601
+ name
602
+ }
603
+ }
604
+ }
605
+ """
606
+
607
+ UPDATE_CYCLE_MUTATION = """
608
+ mutation CycleUpdate($id: String!, $input: CycleUpdateInput!) {
609
+ cycleUpdate(id: $id, input: $input) {
610
+ success
611
+ cycle {
612
+ id
613
+ name
614
+ description
615
+ startsAt
616
+ endsAt
617
+ completedAt
618
+ progress
619
+ completedIssueCount
620
+ issueCount
621
+ }
622
+ }
623
+ }
624
+ """
625
+
626
+ ARCHIVE_CYCLE_MUTATION = """
627
+ mutation CycleArchive($id: String!) {
628
+ cycleArchive(id: $id) {
629
+ success
630
+ }
631
+ }
632
+ """
633
+
634
+ GET_CYCLE_ISSUES_QUERY = """
635
+ query CycleIssues($cycleId: String!, $first: Int!) {
636
+ cycle(id: $cycleId) {
637
+ issues(first: $first) {
638
+ nodes {
639
+ id
640
+ identifier
641
+ title
642
+ description
643
+ state {
644
+ id
645
+ name
646
+ type
647
+ }
648
+ priority
649
+ assignee {
650
+ id
651
+ email
652
+ name
653
+ }
654
+ labels {
655
+ nodes {
656
+ id
657
+ name
658
+ }
659
+ }
660
+ createdAt
661
+ updatedAt
662
+ }
663
+ }
664
+ }
665
+ }
666
+ """
@@ -51,6 +51,34 @@ class LinearStateMapping:
51
51
  "canceled": TicketState.CLOSED,
52
52
  }
53
53
 
54
+ # Semantic state name mappings for flexible workflow matching (1M-552)
55
+ # Maps universal states to common Linear state names (case-insensitive)
56
+ SEMANTIC_NAMES: dict[TicketState, list[str]] = {
57
+ TicketState.OPEN: ["todo", "to do", "open", "new", "backlog"],
58
+ TicketState.READY: ["ready", "triage", "ready for dev", "ready to start"],
59
+ TicketState.TESTED: [
60
+ "tested",
61
+ "in review",
62
+ "review",
63
+ "qa",
64
+ "testing",
65
+ "ready for review",
66
+ ],
67
+ TicketState.WAITING: ["waiting", "on hold", "paused"],
68
+ TicketState.BLOCKED: ["blocked"],
69
+ TicketState.IN_PROGRESS: [
70
+ "in progress",
71
+ "in-progress",
72
+ "started",
73
+ "doing",
74
+ "active",
75
+ "in development",
76
+ "in dev",
77
+ ],
78
+ TicketState.DONE: ["done", "completed", "finished"],
79
+ TicketState.CLOSED: ["closed", "canceled", "cancelled", "won't do", "wont do"],
80
+ }
81
+
54
82
 
55
83
  class LinearWorkflowStateType(Enum):
56
84
  """Linear workflow state types."""
@@ -128,17 +156,99 @@ def get_linear_state_type(state: TicketState) -> str:
128
156
  return LinearStateMapping.TO_LINEAR.get(state, "unstarted")
129
157
 
130
158
 
131
- def get_universal_state(linear_state_type: str) -> TicketState:
132
- """Convert Linear workflow state type to universal TicketState.
159
+ def get_universal_state(
160
+ linear_state_type: str, state_name: str | None = None
161
+ ) -> TicketState:
162
+ """Convert Linear workflow state type to universal TicketState with synonym matching.
163
+
164
+ This function implements intelligent state mapping with fallback strategies:
165
+ 1. Try exact match on state type (backlog, unstarted, started, completed, canceled)
166
+ 2. Try synonym matching on state name (ToDo, In Review, Testing, etc.)
167
+ 3. Default to OPEN for unknown states
168
+
169
+ Synonym Matching Rules (ticket 1M-164, fixed in v2.0.4):
170
+ - "done", "completed", "finished", "resolved" → DONE
171
+ - "closed", "canceled", "cancelled", "won't do" → CLOSED
172
+ - Everything else → OPEN
133
173
 
134
174
  Args:
135
- linear_state_type: Linear workflow state type string
175
+ linear_state_type: Linear workflow state type string (from state.type field)
176
+ state_name: Linear workflow state name (from state.name field, optional)
136
177
 
137
178
  Returns:
138
179
  Universal ticket state enum
139
180
 
140
181
  """
141
- return LinearStateMapping.FROM_LINEAR.get(linear_state_type, TicketState.OPEN)
182
+ # First try exact type match
183
+ if linear_state_type in LinearStateMapping.FROM_LINEAR:
184
+ return LinearStateMapping.FROM_LINEAR[linear_state_type]
185
+
186
+ # If no exact match and state_name provided, try synonym matching
187
+ if state_name:
188
+ state_name_lower = state_name.lower().strip()
189
+
190
+ # DONE states: Work successfully completed
191
+ # - User finished the work
192
+ # - Requirements met
193
+ # - Quality verified
194
+ done_synonyms = [
195
+ "done",
196
+ "completed",
197
+ "finished",
198
+ "resolved",
199
+ ]
200
+
201
+ if any(synonym in state_name_lower for synonym in done_synonyms):
202
+ return TicketState.DONE
203
+
204
+ # CLOSED states: Work terminated without completion
205
+ # - User decided not to do it
206
+ # - Requirements changed
207
+ # - Duplicate/invalid ticket
208
+ closed_synonyms = [
209
+ "closed",
210
+ "cancelled",
211
+ "canceled",
212
+ "won't do",
213
+ "wont do",
214
+ "rejected",
215
+ ]
216
+
217
+ if any(synonym in state_name_lower for synonym in closed_synonyms):
218
+ return TicketState.CLOSED
219
+
220
+ # Check for "in progress" synonyms
221
+ in_progress_synonyms = [
222
+ "in progress",
223
+ "in-progress",
224
+ "working",
225
+ "active",
226
+ "started",
227
+ "doing",
228
+ "in development",
229
+ "in dev",
230
+ ]
231
+
232
+ if any(synonym in state_name_lower for synonym in in_progress_synonyms):
233
+ return TicketState.IN_PROGRESS
234
+
235
+ # Check for "review/testing" synonyms
236
+ review_synonyms = [
237
+ "review",
238
+ "in review",
239
+ "in-review",
240
+ "testing",
241
+ "in test",
242
+ "in-test",
243
+ "qa",
244
+ "ready for review",
245
+ ]
246
+
247
+ if any(synonym in state_name_lower for synonym in review_synonyms):
248
+ return TicketState.READY
249
+
250
+ # Default: everything else is OPEN (including "ToDo", "Backlog", "To Do", etc.)
251
+ return TicketState.OPEN
142
252
 
143
253
 
144
254
  def build_issue_filter(
@@ -147,6 +257,7 @@ def build_issue_filter(
147
257
  priority: Priority | None = None,
148
258
  team_id: str | None = None,
149
259
  project_id: str | None = None,
260
+ parent_id: str | None = None,
150
261
  labels: list[str] | None = None,
151
262
  created_after: str | None = None,
152
263
  updated_after: str | None = None,
@@ -161,6 +272,7 @@ def build_issue_filter(
161
272
  priority: Filter by priority
162
273
  team_id: Filter by team ID
163
274
  project_id: Filter by project ID
275
+ parent_id: Filter by parent issue ID (for listing sub-issues)
164
276
  labels: Filter by label names
165
277
  created_after: Filter by creation date (ISO string)
166
278
  updated_after: Filter by update date (ISO string)
@@ -195,6 +307,10 @@ def build_issue_filter(
195
307
  if project_id:
196
308
  issue_filter["project"] = {"id": {"eq": project_id}}
197
309
 
310
+ # Parent filter (for listing children/sub-issues)
311
+ if parent_id:
312
+ issue_filter["parent"] = {"id": {"eq": parent_id}}
313
+
198
314
  # Labels filter
199
315
  if labels:
200
316
  issue_filter["labels"] = {"some": {"name": {"in": labels}}}
@@ -0,0 +1,56 @@
1
+ """Ticket analysis and cleanup tools for PM monitoring.
2
+
3
+ This module provides comprehensive analysis capabilities for ticket health:
4
+ - Similarity detection: Find duplicate or related tickets using TF-IDF
5
+ - Staleness detection: Identify old, inactive tickets
6
+ - Orphaned detection: Find tickets missing hierarchy (epic/project)
7
+ - Cleanup reports: Comprehensive analysis with recommendations
8
+ - Dependency graph: Build and analyze ticket dependency graphs
9
+ - Health assessment: Assess project health based on ticket metrics
10
+ - Project status: Comprehensive project status analysis and work planning
11
+
12
+ These tools help product managers maintain ticket health and development practices.
13
+
14
+ Note: Some analysis features require optional dependencies (scikit-learn, rapidfuzz).
15
+ Install with: pip install mcp-ticketer[analysis]
16
+ """
17
+
18
+ # Import dependency graph and health assessment (no optional deps required)
19
+ from .dependency_graph import DependencyGraph, DependencyNode
20
+ from .health_assessment import HealthAssessor, HealthMetrics, ProjectHealth
21
+ from .project_status import ProjectStatusResult, StatusAnalyzer, TicketRecommendation
22
+
23
+ # Import optional analysis modules (may fail if dependencies not installed)
24
+ try:
25
+ from .orphaned import OrphanedResult, OrphanedTicketDetector
26
+ from .similarity import SimilarityResult, TicketSimilarityAnalyzer
27
+ from .staleness import StalenessResult, StaleTicketDetector
28
+
29
+ ANALYSIS_AVAILABLE = True
30
+ except ImportError:
31
+ # Set placeholder values when optional deps not available
32
+ OrphanedResult = None # type: ignore
33
+ OrphanedTicketDetector = None # type: ignore
34
+ SimilarityResult = None # type: ignore
35
+ TicketSimilarityAnalyzer = None # type: ignore
36
+ StalenessResult = None # type: ignore
37
+ StaleTicketDetector = None # type: ignore
38
+ ANALYSIS_AVAILABLE = False
39
+
40
+ __all__ = [
41
+ "DependencyGraph",
42
+ "DependencyNode",
43
+ "HealthAssessor",
44
+ "HealthMetrics",
45
+ "ProjectHealth",
46
+ "ProjectStatusResult",
47
+ "StatusAnalyzer",
48
+ "TicketRecommendation",
49
+ "SimilarityResult",
50
+ "TicketSimilarityAnalyzer",
51
+ "StalenessResult",
52
+ "StaleTicketDetector",
53
+ "OrphanedResult",
54
+ "OrphanedTicketDetector",
55
+ "ANALYSIS_AVAILABLE",
56
+ ]