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
|
@@ -59,6 +59,7 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
59
59
|
"""Initialize Asana adapter.
|
|
60
60
|
|
|
61
61
|
Args:
|
|
62
|
+
----
|
|
62
63
|
config: Configuration with:
|
|
63
64
|
- api_key: Asana Personal Access Token (or ASANA_PAT env var)
|
|
64
65
|
- workspace: Asana workspace name (optional, for resolution)
|
|
@@ -68,6 +69,7 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
68
69
|
- max_retries: Maximum retry attempts (default: 3)
|
|
69
70
|
|
|
70
71
|
Raises:
|
|
72
|
+
------
|
|
71
73
|
ValueError: If required configuration is missing
|
|
72
74
|
|
|
73
75
|
"""
|
|
@@ -120,6 +122,7 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
120
122
|
"""Validate Asana API credentials.
|
|
121
123
|
|
|
122
124
|
Returns:
|
|
125
|
+
-------
|
|
123
126
|
Tuple of (is_valid, error_message)
|
|
124
127
|
|
|
125
128
|
"""
|
|
@@ -244,9 +247,11 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
244
247
|
"""Load custom fields configured for a specific project.
|
|
245
248
|
|
|
246
249
|
Args:
|
|
250
|
+
----
|
|
247
251
|
project_gid: Project GID to load custom fields for
|
|
248
252
|
|
|
249
253
|
Returns:
|
|
254
|
+
-------
|
|
250
255
|
Dictionary mapping field name (lowercase) to field data
|
|
251
256
|
|
|
252
257
|
"""
|
|
@@ -277,9 +282,11 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
277
282
|
"""Get custom fields for a project, loading if not cached.
|
|
278
283
|
|
|
279
284
|
Args:
|
|
285
|
+
----
|
|
280
286
|
project_gid: Project GID
|
|
281
287
|
|
|
282
288
|
Returns:
|
|
289
|
+
-------
|
|
283
290
|
Dictionary mapping field name (lowercase) to field data
|
|
284
291
|
|
|
285
292
|
"""
|
|
@@ -295,10 +302,12 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
295
302
|
"""Map TicketState to Asana Status custom field option.
|
|
296
303
|
|
|
297
304
|
Args:
|
|
305
|
+
----
|
|
298
306
|
state: The TicketState to map
|
|
299
307
|
status_field: The Status custom field data with enum_options
|
|
300
308
|
|
|
301
309
|
Returns:
|
|
310
|
+
-------
|
|
302
311
|
Matching enum option or None
|
|
303
312
|
|
|
304
313
|
"""
|
|
@@ -339,6 +348,7 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
339
348
|
We return a mapping to "true"/"false" strings for compatibility.
|
|
340
349
|
|
|
341
350
|
Returns:
|
|
351
|
+
-------
|
|
342
352
|
Dictionary mapping TicketState to completion status string
|
|
343
353
|
|
|
344
354
|
"""
|
|
@@ -357,9 +367,11 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
357
367
|
"""Resolve project identifier (name or GID) to GID.
|
|
358
368
|
|
|
359
369
|
Args:
|
|
370
|
+
----
|
|
360
371
|
project_identifier: Project name or GID
|
|
361
372
|
|
|
362
373
|
Returns:
|
|
374
|
+
-------
|
|
363
375
|
Project GID or None if not found
|
|
364
376
|
|
|
365
377
|
"""
|
|
@@ -392,9 +404,11 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
392
404
|
"""Resolve user identifier (email, name, or GID) to GID.
|
|
393
405
|
|
|
394
406
|
Args:
|
|
407
|
+
----
|
|
395
408
|
user_identifier: User email, name, or GID
|
|
396
409
|
|
|
397
410
|
Returns:
|
|
411
|
+
-------
|
|
398
412
|
User GID or None if not found
|
|
399
413
|
|
|
400
414
|
"""
|
|
@@ -432,12 +446,15 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
432
446
|
"""Create a new Asana project or task.
|
|
433
447
|
|
|
434
448
|
Args:
|
|
449
|
+
----
|
|
435
450
|
ticket: Epic or Task to create
|
|
436
451
|
|
|
437
452
|
Returns:
|
|
453
|
+
-------
|
|
438
454
|
Created ticket with ID populated
|
|
439
455
|
|
|
440
456
|
Raises:
|
|
457
|
+
------
|
|
441
458
|
ValueError: If creation fails
|
|
442
459
|
|
|
443
460
|
"""
|
|
@@ -460,9 +477,11 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
460
477
|
"""Create an Asana project from an Epic.
|
|
461
478
|
|
|
462
479
|
Args:
|
|
480
|
+
----
|
|
463
481
|
epic: Epic to create
|
|
464
482
|
|
|
465
483
|
Returns:
|
|
484
|
+
-------
|
|
466
485
|
Created epic with Asana metadata
|
|
467
486
|
|
|
468
487
|
"""
|
|
@@ -491,9 +510,11 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
491
510
|
subtask (child of another task) when task.parent_issue is provided.
|
|
492
511
|
|
|
493
512
|
Args:
|
|
513
|
+
----
|
|
494
514
|
task: Task to create
|
|
495
515
|
|
|
496
516
|
Returns:
|
|
517
|
+
-------
|
|
497
518
|
Created task with Asana metadata
|
|
498
519
|
|
|
499
520
|
"""
|
|
@@ -564,6 +585,7 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
564
585
|
"""Add tags to an Asana task.
|
|
565
586
|
|
|
566
587
|
Args:
|
|
588
|
+
----
|
|
567
589
|
task_gid: Task GID
|
|
568
590
|
tags: List of tag names to add
|
|
569
591
|
|
|
@@ -602,9 +624,11 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
602
624
|
"""Read an Asana task by GID.
|
|
603
625
|
|
|
604
626
|
Args:
|
|
627
|
+
----
|
|
605
628
|
ticket_id: Asana task GID
|
|
606
629
|
|
|
607
630
|
Returns:
|
|
631
|
+
-------
|
|
608
632
|
Task if found, None otherwise
|
|
609
633
|
|
|
610
634
|
"""
|
|
@@ -632,10 +656,12 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
632
656
|
"""Update an Asana task.
|
|
633
657
|
|
|
634
658
|
Args:
|
|
659
|
+
----
|
|
635
660
|
ticket_id: Task GID
|
|
636
661
|
updates: Dictionary of fields to update
|
|
637
662
|
|
|
638
663
|
Returns:
|
|
664
|
+
-------
|
|
639
665
|
Updated task or None if not found
|
|
640
666
|
|
|
641
667
|
"""
|
|
@@ -776,9 +802,11 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
776
802
|
"""Delete an Asana task.
|
|
777
803
|
|
|
778
804
|
Args:
|
|
805
|
+
----
|
|
779
806
|
ticket_id: Task GID
|
|
780
807
|
|
|
781
808
|
Returns:
|
|
809
|
+
-------
|
|
782
810
|
True if successfully deleted
|
|
783
811
|
|
|
784
812
|
"""
|
|
@@ -795,11 +823,13 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
795
823
|
"""List Asana tasks with optional filtering.
|
|
796
824
|
|
|
797
825
|
Args:
|
|
826
|
+
----
|
|
798
827
|
limit: Maximum number of tasks to return
|
|
799
828
|
offset: Number of tasks to skip (Note: Asana uses offset tokens)
|
|
800
829
|
filters: Optional filters (state, assignee, project, etc.)
|
|
801
830
|
|
|
802
831
|
Returns:
|
|
832
|
+
-------
|
|
803
833
|
List of tasks matching the criteria
|
|
804
834
|
|
|
805
835
|
"""
|
|
@@ -896,9 +926,11 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
896
926
|
"""Search Asana tasks using filters.
|
|
897
927
|
|
|
898
928
|
Args:
|
|
929
|
+
----
|
|
899
930
|
query: Search query with filters
|
|
900
931
|
|
|
901
932
|
Returns:
|
|
933
|
+
-------
|
|
902
934
|
List of tasks matching the search criteria
|
|
903
935
|
|
|
904
936
|
"""
|
|
@@ -936,10 +968,12 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
936
968
|
"""Transition task to new state.
|
|
937
969
|
|
|
938
970
|
Args:
|
|
971
|
+
----
|
|
939
972
|
ticket_id: Task GID
|
|
940
973
|
target_state: Target state
|
|
941
974
|
|
|
942
975
|
Returns:
|
|
976
|
+
-------
|
|
943
977
|
Updated task or None if failed
|
|
944
978
|
|
|
945
979
|
"""
|
|
@@ -949,12 +983,15 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
949
983
|
"""Add a comment to an Asana task (as a story).
|
|
950
984
|
|
|
951
985
|
Args:
|
|
986
|
+
----
|
|
952
987
|
comment: Comment to add
|
|
953
988
|
|
|
954
989
|
Returns:
|
|
990
|
+
-------
|
|
955
991
|
Created comment with ID
|
|
956
992
|
|
|
957
993
|
Raises:
|
|
994
|
+
------
|
|
958
995
|
ValueError: If comment creation fails
|
|
959
996
|
|
|
960
997
|
"""
|
|
@@ -984,11 +1021,13 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
984
1021
|
Filters stories to only return comment type (not system events).
|
|
985
1022
|
|
|
986
1023
|
Args:
|
|
1024
|
+
----
|
|
987
1025
|
ticket_id: Task GID
|
|
988
1026
|
limit: Maximum number of comments to return
|
|
989
1027
|
offset: Number of comments to skip
|
|
990
1028
|
|
|
991
1029
|
Returns:
|
|
1030
|
+
-------
|
|
992
1031
|
List of comments for the task
|
|
993
1032
|
|
|
994
1033
|
"""
|
|
@@ -1019,11 +1058,13 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
1019
1058
|
"""Create an Asana project (Epic).
|
|
1020
1059
|
|
|
1021
1060
|
Args:
|
|
1061
|
+
----
|
|
1022
1062
|
title: Epic title
|
|
1023
1063
|
description: Epic description
|
|
1024
1064
|
**kwargs: Additional fields
|
|
1025
1065
|
|
|
1026
1066
|
Returns:
|
|
1067
|
+
-------
|
|
1027
1068
|
Created epic or None if failed
|
|
1028
1069
|
|
|
1029
1070
|
"""
|
|
@@ -1041,9 +1082,11 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
1041
1082
|
"""Get an Asana project (Epic) by GID.
|
|
1042
1083
|
|
|
1043
1084
|
Args:
|
|
1085
|
+
----
|
|
1044
1086
|
epic_id: Project GID
|
|
1045
1087
|
|
|
1046
1088
|
Returns:
|
|
1089
|
+
-------
|
|
1047
1090
|
Epic if found, None otherwise
|
|
1048
1091
|
|
|
1049
1092
|
"""
|
|
@@ -1065,10 +1108,12 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
1065
1108
|
"""Update an Asana project (Epic).
|
|
1066
1109
|
|
|
1067
1110
|
Args:
|
|
1111
|
+
----
|
|
1068
1112
|
epic_id: Project GID
|
|
1069
1113
|
updates: Dictionary of fields to update
|
|
1070
1114
|
|
|
1071
1115
|
Returns:
|
|
1116
|
+
-------
|
|
1072
1117
|
Updated epic or None if failed
|
|
1073
1118
|
|
|
1074
1119
|
"""
|
|
@@ -1108,9 +1153,11 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
1108
1153
|
"""List all Asana projects (Epics).
|
|
1109
1154
|
|
|
1110
1155
|
Args:
|
|
1156
|
+
----
|
|
1111
1157
|
**kwargs: Optional filter parameters
|
|
1112
1158
|
|
|
1113
1159
|
Returns:
|
|
1160
|
+
-------
|
|
1114
1161
|
List of epics
|
|
1115
1162
|
|
|
1116
1163
|
"""
|
|
@@ -1143,13 +1190,65 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
1143
1190
|
logger.error(f"Failed to list projects: {e}")
|
|
1144
1191
|
return []
|
|
1145
1192
|
|
|
1193
|
+
async def delete_epic(self, epic_id: str) -> bool:
|
|
1194
|
+
"""Delete an Asana project (Epic).
|
|
1195
|
+
|
|
1196
|
+
Args:
|
|
1197
|
+
----
|
|
1198
|
+
epic_id: Project GID to delete
|
|
1199
|
+
|
|
1200
|
+
Returns:
|
|
1201
|
+
-------
|
|
1202
|
+
True if successfully deleted, False otherwise
|
|
1203
|
+
|
|
1204
|
+
Raises:
|
|
1205
|
+
------
|
|
1206
|
+
ValueError: If credentials are invalid or GID format is invalid
|
|
1207
|
+
|
|
1208
|
+
"""
|
|
1209
|
+
# Validate credentials
|
|
1210
|
+
is_valid, error_message = self.validate_credentials()
|
|
1211
|
+
if not is_valid:
|
|
1212
|
+
raise ValueError(error_message)
|
|
1213
|
+
|
|
1214
|
+
# Validate GID format (should be numeric)
|
|
1215
|
+
if not epic_id or not epic_id.isdigit():
|
|
1216
|
+
raise ValueError(
|
|
1217
|
+
f"Invalid project GID '{epic_id}'. Asana project GIDs must be numeric."
|
|
1218
|
+
)
|
|
1219
|
+
|
|
1220
|
+
try:
|
|
1221
|
+
# Delete project using REST API
|
|
1222
|
+
await self.client.delete(f"/projects/{epic_id}")
|
|
1223
|
+
logger.info(f"Successfully deleted project {epic_id}")
|
|
1224
|
+
return True
|
|
1225
|
+
|
|
1226
|
+
except Exception as e:
|
|
1227
|
+
# Check if it's a 404 (not found) - return False
|
|
1228
|
+
if "404" in str(e) or "Not Found" in str(e):
|
|
1229
|
+
logger.warning(f"Project {epic_id} not found")
|
|
1230
|
+
return False
|
|
1231
|
+
|
|
1232
|
+
# Check for permissions errors
|
|
1233
|
+
if "403" in str(e) or "Forbidden" in str(e):
|
|
1234
|
+
logger.error(f"Permission denied to delete project {epic_id}")
|
|
1235
|
+
raise ValueError(
|
|
1236
|
+
f"Permission denied: You don't have permission to delete project {epic_id}"
|
|
1237
|
+
) from e
|
|
1238
|
+
|
|
1239
|
+
# Other errors - log and raise
|
|
1240
|
+
logger.error(f"Failed to delete project {epic_id}: {e}")
|
|
1241
|
+
raise ValueError(f"Failed to delete project: {e}") from e
|
|
1242
|
+
|
|
1146
1243
|
async def list_issues_by_epic(self, epic_id: str) -> builtins.list[Task]:
|
|
1147
1244
|
"""List all tasks in a project (Epic).
|
|
1148
1245
|
|
|
1149
1246
|
Args:
|
|
1247
|
+
----
|
|
1150
1248
|
epic_id: Project GID
|
|
1151
1249
|
|
|
1152
1250
|
Returns:
|
|
1251
|
+
-------
|
|
1153
1252
|
List of tasks in the project
|
|
1154
1253
|
|
|
1155
1254
|
"""
|
|
@@ -1161,9 +1260,11 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
1161
1260
|
"""List all subtasks of a task (Issue).
|
|
1162
1261
|
|
|
1163
1262
|
Args:
|
|
1263
|
+
----
|
|
1164
1264
|
issue_id: Parent task GID
|
|
1165
1265
|
|
|
1166
1266
|
Returns:
|
|
1267
|
+
-------
|
|
1167
1268
|
List of subtasks
|
|
1168
1269
|
|
|
1169
1270
|
"""
|
|
@@ -1198,14 +1299,17 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
1198
1299
|
"""Attach a file to an Asana task.
|
|
1199
1300
|
|
|
1200
1301
|
Args:
|
|
1302
|
+
----
|
|
1201
1303
|
ticket_id: Task GID
|
|
1202
1304
|
file_path: Local file path to upload
|
|
1203
1305
|
description: Optional attachment description (not used by Asana)
|
|
1204
1306
|
|
|
1205
1307
|
Returns:
|
|
1308
|
+
-------
|
|
1206
1309
|
Created Attachment with metadata
|
|
1207
1310
|
|
|
1208
1311
|
Raises:
|
|
1312
|
+
------
|
|
1209
1313
|
FileNotFoundError: If file doesn't exist
|
|
1210
1314
|
ValueError: If upload fails
|
|
1211
1315
|
|
|
@@ -1255,9 +1359,11 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
1255
1359
|
"""Get all attachments for an Asana task.
|
|
1256
1360
|
|
|
1257
1361
|
Args:
|
|
1362
|
+
----
|
|
1258
1363
|
ticket_id: Task GID
|
|
1259
1364
|
|
|
1260
1365
|
Returns:
|
|
1366
|
+
-------
|
|
1261
1367
|
List of attachments
|
|
1262
1368
|
|
|
1263
1369
|
"""
|
|
@@ -1285,10 +1391,12 @@ class AsanaAdapter(BaseAdapter[Task]):
|
|
|
1285
1391
|
"""Delete an attachment from an Asana task.
|
|
1286
1392
|
|
|
1287
1393
|
Args:
|
|
1394
|
+
----
|
|
1288
1395
|
ticket_id: Task GID (not used, kept for interface compatibility)
|
|
1289
1396
|
attachment_id: Attachment GID
|
|
1290
1397
|
|
|
1291
1398
|
Returns:
|
|
1399
|
+
-------
|
|
1292
1400
|
True if deleted successfully
|
|
1293
1401
|
|
|
1294
1402
|
"""
|
|
@@ -22,9 +22,11 @@ def parse_asana_datetime(date_str: str | None) -> datetime | None:
|
|
|
22
22
|
"""Parse Asana datetime string to datetime object.
|
|
23
23
|
|
|
24
24
|
Args:
|
|
25
|
+
----
|
|
25
26
|
date_str: ISO 8601 datetime string or None
|
|
26
27
|
|
|
27
28
|
Returns:
|
|
29
|
+
-------
|
|
28
30
|
Parsed datetime or None
|
|
29
31
|
|
|
30
32
|
"""
|
|
@@ -43,9 +45,11 @@ def map_asana_project_to_epic(project: dict[str, Any]) -> Epic:
|
|
|
43
45
|
"""Map Asana project to Epic.
|
|
44
46
|
|
|
45
47
|
Args:
|
|
48
|
+
----
|
|
46
49
|
project: Asana project data
|
|
47
50
|
|
|
48
51
|
Returns:
|
|
52
|
+
-------
|
|
49
53
|
Epic model instance
|
|
50
54
|
|
|
51
55
|
"""
|
|
@@ -91,9 +95,11 @@ def map_asana_task_to_task(task: dict[str, Any]) -> Task:
|
|
|
91
95
|
- No parent task → ISSUE (standard task)
|
|
92
96
|
|
|
93
97
|
Args:
|
|
98
|
+
----
|
|
94
99
|
task: Asana task data
|
|
95
100
|
|
|
96
101
|
Returns:
|
|
102
|
+
-------
|
|
97
103
|
Task model instance
|
|
98
104
|
|
|
99
105
|
"""
|
|
@@ -181,11 +187,13 @@ def map_epic_to_asana_project(
|
|
|
181
187
|
"""Map Epic to Asana project create/update data.
|
|
182
188
|
|
|
183
189
|
Args:
|
|
190
|
+
----
|
|
184
191
|
epic: Epic model instance
|
|
185
192
|
workspace_gid: Asana workspace GID
|
|
186
193
|
team_gid: Asana team GID (optional, required for organization workspaces)
|
|
187
194
|
|
|
188
195
|
Returns:
|
|
196
|
+
-------
|
|
189
197
|
Asana project data for create/update
|
|
190
198
|
|
|
191
199
|
"""
|
|
@@ -216,11 +224,13 @@ def map_task_to_asana_task(
|
|
|
216
224
|
"""Map Task to Asana task create/update data.
|
|
217
225
|
|
|
218
226
|
Args:
|
|
227
|
+
----
|
|
219
228
|
task: Task model instance
|
|
220
229
|
workspace_gid: Asana workspace GID
|
|
221
230
|
project_gids: List of project GIDs to add task to (optional)
|
|
222
231
|
|
|
223
232
|
Returns:
|
|
233
|
+
-------
|
|
224
234
|
Asana task data for create/update
|
|
225
235
|
|
|
226
236
|
"""
|
|
@@ -262,10 +272,12 @@ def map_asana_story_to_comment(story: dict[str, Any], task_gid: str) -> Comment
|
|
|
262
272
|
Only maps stories of type 'comment'. Other story types (system events) are filtered out.
|
|
263
273
|
|
|
264
274
|
Args:
|
|
275
|
+
----
|
|
265
276
|
story: Asana story data
|
|
266
277
|
task_gid: Parent task GID
|
|
267
278
|
|
|
268
279
|
Returns:
|
|
280
|
+
-------
|
|
269
281
|
Comment model instance or None if not a comment type
|
|
270
282
|
|
|
271
283
|
"""
|
|
@@ -300,10 +312,12 @@ def map_asana_attachment_to_attachment(
|
|
|
300
312
|
IMPORTANT: Use permanent_url for reliable access, not download_url which expires.
|
|
301
313
|
|
|
302
314
|
Args:
|
|
315
|
+
----
|
|
303
316
|
attachment: Asana attachment data
|
|
304
317
|
task_gid: Parent task GID
|
|
305
318
|
|
|
306
319
|
Returns:
|
|
320
|
+
-------
|
|
307
321
|
Attachment model instance
|
|
308
322
|
|
|
309
323
|
"""
|