@fink-andreas/pi-linear-tools 0.4.3 → 0.5.1

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.
package/src/linear.js CHANGED
@@ -153,6 +153,64 @@ const PROJECTS_LOOKUP_QUERY = `
153
153
  }
154
154
  `;
155
155
 
156
+ const PROJECT_MINIMAL_QUERY = `
157
+ query ProjectMinimal($id: String!) {
158
+ project(id: $id) {
159
+ id
160
+ name
161
+ slugId
162
+ archivedAt
163
+ }
164
+ }
165
+ `;
166
+
167
+ const PROJECT_MILESTONES_QUERY = `
168
+ query ProjectMilestones($id: String!, $first: Int!) {
169
+ project(id: $id) {
170
+ id
171
+ name
172
+ projectMilestones(first: $first) {
173
+ nodes {
174
+ id
175
+ name
176
+ description
177
+ progress
178
+ sortOrder
179
+ targetDate
180
+ status
181
+ }
182
+ }
183
+ }
184
+ }
185
+ `;
186
+
187
+ const TEAM_MINIMAL_QUERY = `
188
+ query TeamMinimal($id: String!) {
189
+ team(id: $id) {
190
+ id
191
+ key
192
+ name
193
+ }
194
+ }
195
+ `;
196
+
197
+ const TEAM_STATES_QUERY = `
198
+ query TeamStates($id: String!, $first: Int!) {
199
+ team(id: $id) {
200
+ id
201
+ key
202
+ name
203
+ states(first: $first) {
204
+ nodes {
205
+ id
206
+ name
207
+ type
208
+ }
209
+ }
210
+ }
211
+ }
212
+ `;
213
+
156
214
  const PROJECT_CREATE_MUTATION = `
157
215
  mutation ProjectCreate($input: ProjectCreateInput!) {
158
216
  projectCreate(input: $input) {
@@ -349,12 +407,383 @@ const DOCUMENT_CREATE_MUTATION = `
349
407
  }
350
408
  `;
351
409
 
352
- const DOCUMENT_UPDATE_MUTATION = `
353
- mutation DocumentUpdate($id: String!, $input: DocumentUpdateInput!) {
354
- documentUpdate(id: $id, input: $input) {
410
+ const DOCUMENT_UPDATE_MUTATION = `
411
+ mutation DocumentUpdate($id: String!, $input: DocumentUpdateInput!) {
412
+ documentUpdate(id: $id, input: $input) {
413
+ success
414
+ document {
415
+ id
416
+ }
417
+ }
418
+ }
419
+ `;
420
+
421
+ const ISSUE_MINIMAL_QUERY = `
422
+ query IssueMinimal($id: String!) {
423
+ issue(id: $id) {
424
+ id
425
+ identifier
426
+ title
427
+ description
428
+ url
429
+ branchName
430
+ priority
431
+ estimate
432
+ createdAt
433
+ updatedAt
434
+ state {
435
+ id
436
+ name
437
+ type
438
+ }
439
+ team {
440
+ id
441
+ key
442
+ name
443
+ }
444
+ project {
445
+ id
446
+ name
447
+ }
448
+ projectMilestone {
449
+ id
450
+ name
451
+ }
452
+ assignee {
453
+ id
454
+ name
455
+ displayName
456
+ }
457
+ }
458
+ }
459
+ `;
460
+
461
+ const ISSUE_MINIMAL_BY_TEAM_AND_NUMBER_QUERY = `
462
+ query IssueMinimalByTeamAndNumber($teamKey: String!, $number: Float!) {
463
+ issues(first: 1, filter: { team: { key: { eq: $teamKey } }, number: { eq: $number } }) {
464
+ nodes {
465
+ id
466
+ identifier
467
+ title
468
+ description
469
+ url
470
+ branchName
471
+ priority
472
+ estimate
473
+ createdAt
474
+ updatedAt
475
+ state {
476
+ id
477
+ name
478
+ type
479
+ }
480
+ team {
481
+ id
482
+ key
483
+ name
484
+ }
485
+ project {
486
+ id
487
+ name
488
+ }
489
+ projectMilestone {
490
+ id
491
+ name
492
+ }
493
+ assignee {
494
+ id
495
+ name
496
+ displayName
497
+ }
498
+ }
499
+ }
500
+ }
501
+ `;
502
+
503
+ const ISSUE_DETAILS_QUERY = `
504
+ query IssueDetails($id: String!) {
505
+ issue(id: $id) {
506
+ id
507
+ identifier
508
+ title
509
+ description
510
+ url
511
+ branchName
512
+ priority
513
+ estimate
514
+ createdAt
515
+ updatedAt
516
+ state {
517
+ id
518
+ name
519
+ color
520
+ type
521
+ }
522
+ team {
523
+ id
524
+ key
525
+ name
526
+ }
527
+ project {
528
+ id
529
+ name
530
+ }
531
+ projectMilestone {
532
+ id
533
+ name
534
+ }
535
+ assignee {
536
+ id
537
+ name
538
+ displayName
539
+ }
540
+ creator {
541
+ id
542
+ name
543
+ displayName
544
+ }
545
+ labels(first: 50) {
546
+ nodes {
547
+ id
548
+ name
549
+ color
550
+ }
551
+ }
552
+ parent {
553
+ id
554
+ identifier
555
+ title
556
+ state {
557
+ id
558
+ name
559
+ color
560
+ }
561
+ }
562
+ children(first: 50) {
563
+ nodes {
564
+ id
565
+ identifier
566
+ title
567
+ state {
568
+ id
569
+ name
570
+ color
571
+ }
572
+ }
573
+ }
574
+ attachments(first: 50) {
575
+ nodes {
576
+ id
577
+ title
578
+ url
579
+ subtitle
580
+ sourceType
581
+ createdAt
582
+ }
583
+ }
584
+ }
585
+ }
586
+ `;
587
+
588
+ const ISSUE_DETAILS_WITH_COMMENTS_QUERY = `
589
+ query IssueDetailsWithComments($id: String!) {
590
+ issue(id: $id) {
591
+ id
592
+ identifier
593
+ title
594
+ description
595
+ url
596
+ branchName
597
+ priority
598
+ estimate
599
+ createdAt
600
+ updatedAt
601
+ state {
602
+ id
603
+ name
604
+ color
605
+ type
606
+ }
607
+ team {
608
+ id
609
+ key
610
+ name
611
+ }
612
+ project {
613
+ id
614
+ name
615
+ }
616
+ projectMilestone {
617
+ id
618
+ name
619
+ }
620
+ assignee {
621
+ id
622
+ name
623
+ displayName
624
+ }
625
+ creator {
626
+ id
627
+ name
628
+ displayName
629
+ }
630
+ labels(first: 50) {
631
+ nodes {
632
+ id
633
+ name
634
+ color
635
+ }
636
+ }
637
+ parent {
638
+ id
639
+ identifier
640
+ title
641
+ state {
642
+ id
643
+ name
644
+ color
645
+ }
646
+ }
647
+ children(first: 50) {
648
+ nodes {
649
+ id
650
+ identifier
651
+ title
652
+ state {
653
+ id
654
+ name
655
+ color
656
+ }
657
+ }
658
+ }
659
+ comments(first: 100) {
660
+ nodes {
661
+ id
662
+ body
663
+ createdAt
664
+ updatedAt
665
+ user {
666
+ id
667
+ name
668
+ displayName
669
+ }
670
+ externalUser {
671
+ id
672
+ name
673
+ displayName
674
+ }
675
+ parent {
676
+ id
677
+ }
678
+ }
679
+ }
680
+ attachments(first: 50) {
681
+ nodes {
682
+ id
683
+ title
684
+ url
685
+ subtitle
686
+ sourceType
687
+ createdAt
688
+ }
689
+ }
690
+ }
691
+ }
692
+ `;
693
+
694
+ const ISSUE_CREATE_MUTATION = `
695
+ mutation IssueCreate($input: IssueCreateInput!) {
696
+ issueCreate(input: $input) {
697
+ success
698
+ issue {
699
+ id
700
+ identifier
701
+ title
702
+ description
703
+ url
704
+ branchName
705
+ priority
706
+ estimate
707
+ createdAt
708
+ updatedAt
709
+ state {
710
+ id
711
+ name
712
+ type
713
+ }
714
+ team {
715
+ id
716
+ key
717
+ name
718
+ }
719
+ project {
720
+ id
721
+ name
722
+ }
723
+ projectMilestone {
724
+ id
725
+ name
726
+ }
727
+ assignee {
728
+ id
729
+ name
730
+ displayName
731
+ }
732
+ }
733
+ }
734
+ }
735
+ `;
736
+
737
+ const ISSUE_UPDATE_MUTATION = `
738
+ mutation IssueUpdate($id: String!, $input: IssueUpdateInput!) {
739
+ issueUpdate(id: $id, input: $input) {
740
+ success
741
+ issue {
742
+ id
743
+ identifier
744
+ title
745
+ description
746
+ url
747
+ branchName
748
+ priority
749
+ estimate
750
+ createdAt
751
+ updatedAt
752
+ state {
753
+ id
754
+ name
755
+ type
756
+ }
757
+ team {
758
+ id
759
+ key
760
+ name
761
+ }
762
+ project {
763
+ id
764
+ name
765
+ }
766
+ projectMilestone {
767
+ id
768
+ name
769
+ }
770
+ assignee {
771
+ id
772
+ name
773
+ displayName
774
+ }
775
+ }
776
+ }
777
+ }
778
+ `;
779
+
780
+ const ISSUE_DELETE_MUTATION = `
781
+ mutation IssueDelete($id: String!) {
782
+ issueDelete(id: $id) {
355
783
  success
356
- document {
784
+ entity {
357
785
  id
786
+ identifier
358
787
  }
359
788
  }
360
789
  }
@@ -490,10 +919,15 @@ async function executeOptimizedQuery(client, query, variables) {
490
919
  };
491
920
  }
492
921
 
493
- async function executeGraphQL(client, query, variables = {}) {
494
- const rawRequest =
922
+ function getRawRequest(client) {
923
+ return (
495
924
  (typeof client.client?.rawRequest === 'function' ? client.client.rawRequest.bind(client.client) : null) ||
496
- (typeof client.rawRequest === 'function' ? client.rawRequest.bind(client) : null);
925
+ (typeof client.rawRequest === 'function' ? client.rawRequest.bind(client) : null)
926
+ );
927
+ }
928
+
929
+ async function executeGraphQL(client, query, variables = {}) {
930
+ const rawRequest = getRawRequest(client);
497
931
 
498
932
  if (!rawRequest) {
499
933
  throw new Error('GraphQL rawRequest is unavailable on this Linear client');
@@ -520,6 +954,9 @@ function transformRawIssue(rawIssue) {
520
954
  url: rawIssue.url,
521
955
  branchName: rawIssue.branchName,
522
956
  priority: rawIssue.priority,
957
+ estimate: rawIssue.estimate ?? null,
958
+ createdAt: rawIssue.createdAt ?? null,
959
+ updatedAt: rawIssue.updatedAt ?? null,
523
960
  state: rawIssue.state ? { id: rawIssue.state.id, name: rawIssue.state.name, type: rawIssue.state.type } : null,
524
961
  team: rawIssue.team ? { id: rawIssue.team.id, key: rawIssue.team.key, name: rawIssue.team.name } : null,
525
962
  project: rawIssue.project ? { id: rawIssue.project.id, name: rawIssue.project.name } : null,
@@ -528,6 +965,288 @@ function transformRawIssue(rawIssue) {
528
965
  };
529
966
  }
530
967
 
968
+ function parseIssueIdentifierLookup(lookup) {
969
+ const match = String(lookup || '').trim().match(/^([A-Za-z0-9]+)-(\d+)$/);
970
+ if (!match) {
971
+ return null;
972
+ }
973
+
974
+ return {
975
+ teamKey: match[1].toUpperCase(),
976
+ number: Number.parseInt(match[2], 10),
977
+ };
978
+ }
979
+
980
+ async function fetchIssueMinimalById(client, issueId) {
981
+ if (getRawRequest(client)) {
982
+ try {
983
+ const data = await executeGraphQL(client, ISSUE_MINIMAL_QUERY, { id: issueId });
984
+ const transformed = transformRawIssue(data?.issue ?? null);
985
+ if (transformed) {
986
+ return transformed;
987
+ }
988
+ } catch {
989
+ // Fall back to SDK read below.
990
+ }
991
+ }
992
+
993
+ const sdkIssue = await client.issue?.(issueId);
994
+ return sdkIssue ? transformIssue(sdkIssue) : null;
995
+ }
996
+
997
+ async function fetchIssueMinimalByIdentifier(client, identifier) {
998
+ if (getRawRequest(client)) {
999
+ const parsed = parseIssueIdentifierLookup(identifier);
1000
+ if (parsed) {
1001
+ try {
1002
+ const data = await executeGraphQL(client, ISSUE_MINIMAL_BY_TEAM_AND_NUMBER_QUERY, parsed);
1003
+ const transformed = transformRawIssue(data?.issues?.nodes?.[0] ?? null);
1004
+ if (transformed) {
1005
+ return transformed;
1006
+ }
1007
+ } catch {
1008
+ // Fall back to SDK read below.
1009
+ }
1010
+ }
1011
+ }
1012
+
1013
+ const sdkIssue = await client.issue?.(identifier);
1014
+ return sdkIssue ? transformIssue(sdkIssue) : null;
1015
+ }
1016
+
1017
+ async function fetchIssueMinimal(client, lookup) {
1018
+ return isLinearId(lookup)
1019
+ ? fetchIssueMinimalById(client, lookup)
1020
+ : fetchIssueMinimalByIdentifier(client, lookup);
1021
+ }
1022
+
1023
+ async function performIssueUpdate(client, issueId, updateInput) {
1024
+ if (getRawRequest(client)) {
1025
+ const payload = await executeGraphQL(client, ISSUE_UPDATE_MUTATION, {
1026
+ id: issueId,
1027
+ input: updateInput,
1028
+ });
1029
+
1030
+ if (!payload?.issueUpdate?.success) {
1031
+ throw new Error('Failed to update issue');
1032
+ }
1033
+
1034
+ return transformRawIssue(payload.issueUpdate.issue ?? null);
1035
+ }
1036
+
1037
+ const sdkIssue = await client.issue(issueId);
1038
+ if (!sdkIssue) {
1039
+ throw new Error(`Issue not found: ${issueId}`);
1040
+ }
1041
+
1042
+ const result = await sdkIssue.update(updateInput);
1043
+ if (!result.success) {
1044
+ throw new Error('Failed to update issue');
1045
+ }
1046
+
1047
+ return transformIssue(result.issue);
1048
+ }
1049
+
1050
+ function transformRawIssueDetails(rawIssue, options = {}) {
1051
+ const { includeComments = true } = options;
1052
+ if (!rawIssue) return null;
1053
+
1054
+ return {
1055
+ identifier: rawIssue.identifier,
1056
+ title: rawIssue.title,
1057
+ description: rawIssue.description ?? null,
1058
+ url: rawIssue.url ?? null,
1059
+ branchName: rawIssue.branchName ?? null,
1060
+ priority: rawIssue.priority ?? null,
1061
+ estimate: rawIssue.estimate ?? null,
1062
+ createdAt: rawIssue.createdAt ?? null,
1063
+ updatedAt: rawIssue.updatedAt ?? null,
1064
+ state: rawIssue.state ? {
1065
+ name: rawIssue.state.name,
1066
+ color: rawIssue.state.color ?? null,
1067
+ type: rawIssue.state.type ?? null,
1068
+ } : null,
1069
+ team: rawIssue.team ? { id: rawIssue.team.id, key: rawIssue.team.key, name: rawIssue.team.name } : null,
1070
+ project: rawIssue.project ? { id: rawIssue.project.id, name: rawIssue.project.name } : null,
1071
+ projectMilestone: rawIssue.projectMilestone ? { id: rawIssue.projectMilestone.id, name: rawIssue.projectMilestone.name } : null,
1072
+ assignee: rawIssue.assignee ? { id: rawIssue.assignee.id, name: rawIssue.assignee.name, displayName: rawIssue.assignee.displayName } : null,
1073
+ creator: rawIssue.creator ? { id: rawIssue.creator.id, name: rawIssue.creator.name, displayName: rawIssue.creator.displayName } : null,
1074
+ labels: (rawIssue.labels?.nodes || []).map((label) => ({
1075
+ id: label.id,
1076
+ name: label.name,
1077
+ color: label.color ?? null,
1078
+ })),
1079
+ parent: rawIssue.parent ? {
1080
+ identifier: rawIssue.parent.identifier,
1081
+ title: rawIssue.parent.title,
1082
+ state: rawIssue.parent.state ? {
1083
+ name: rawIssue.parent.state.name,
1084
+ color: rawIssue.parent.state.color ?? null,
1085
+ } : null,
1086
+ } : null,
1087
+ children: (rawIssue.children?.nodes || []).map((child) => ({
1088
+ identifier: child.identifier,
1089
+ title: child.title,
1090
+ state: child.state ? {
1091
+ name: child.state.name,
1092
+ color: child.state.color ?? null,
1093
+ } : null,
1094
+ })),
1095
+ comments: includeComments
1096
+ ? (rawIssue.comments?.nodes || []).map((comment) => ({
1097
+ id: comment.id,
1098
+ body: comment.body,
1099
+ createdAt: comment.createdAt,
1100
+ updatedAt: comment.updatedAt,
1101
+ user: comment.user ? {
1102
+ name: comment.user.name,
1103
+ displayName: comment.user.displayName,
1104
+ } : null,
1105
+ externalUser: comment.externalUser ? {
1106
+ name: comment.externalUser.name,
1107
+ displayName: comment.externalUser.displayName,
1108
+ } : null,
1109
+ parent: comment.parent ? { id: comment.parent.id } : null,
1110
+ }))
1111
+ : [],
1112
+ attachments: (rawIssue.attachments?.nodes || []).map((attachment) => ({
1113
+ id: attachment.id,
1114
+ title: attachment.title,
1115
+ url: attachment.url,
1116
+ subtitle: attachment.subtitle,
1117
+ sourceType: attachment.sourceType,
1118
+ createdAt: attachment.createdAt,
1119
+ })),
1120
+ };
1121
+ }
1122
+
1123
+ function transformRawProjectMinimal(rawProject) {
1124
+ if (!rawProject) return null;
1125
+
1126
+ return {
1127
+ id: rawProject.id,
1128
+ name: rawProject.name,
1129
+ slugId: rawProject.slugId ?? null,
1130
+ archivedAt: rawProject.archivedAt ?? null,
1131
+ };
1132
+ }
1133
+
1134
+ function transformRawMilestone(rawMilestone, project = null) {
1135
+ if (!rawMilestone) return null;
1136
+
1137
+ return {
1138
+ id: rawMilestone.id,
1139
+ name: rawMilestone.name,
1140
+ description: rawMilestone.description ?? null,
1141
+ progress: rawMilestone.progress ?? null,
1142
+ order: rawMilestone.sortOrder ?? null,
1143
+ targetDate: rawMilestone.targetDate ?? null,
1144
+ status: rawMilestone.status ?? null,
1145
+ project: project ? { id: project.id, name: project.name } : null,
1146
+ };
1147
+ }
1148
+
1149
+ async function fetchProjectMinimal(client, projectId) {
1150
+ // If input looks like a UUID but can't be resolved directly,
1151
+ // it might be a project name that accidentally matches the UUID pattern.
1152
+ // Return null to signal that resolveProjectRef should fall back to name search.
1153
+ if (isLinearId(projectId)) {
1154
+ if (!getRawRequest(client)) {
1155
+ const sdkProject = await client.project?.(projectId);
1156
+ if (sdkProject) {
1157
+ return transformRawProjectMinimal(sdkProject);
1158
+ }
1159
+ // SDK can't resolve it - return null so resolveProjectRef falls back to name lookup
1160
+ return null;
1161
+ }
1162
+
1163
+ const data = await executeGraphQL(client, PROJECT_MINIMAL_QUERY, { id: projectId });
1164
+ if (data?.project) {
1165
+ return transformRawProjectMinimal(data.project);
1166
+ }
1167
+ // GraphQL can't resolve it - return null so resolveProjectRef falls back to name lookup
1168
+ return null;
1169
+ }
1170
+
1171
+ // For non-UUID inputs (names), return null to let resolveProjectRef handle it directly
1172
+ return null;
1173
+ }
1174
+
1175
+ async function fetchTeamMinimal(client, teamId) {
1176
+ if (getRawRequest(client)) {
1177
+ try {
1178
+ const data = await executeGraphQL(client, TEAM_MINIMAL_QUERY, { id: teamId });
1179
+ const team = data?.team;
1180
+ if (team) {
1181
+ return { id: team.id, key: team.key, name: team.name };
1182
+ }
1183
+ } catch {
1184
+ // Fall back to SDK read below.
1185
+ }
1186
+ }
1187
+
1188
+ const sdkTeam = await client.team?.(teamId);
1189
+ return sdkTeam ? { id: sdkTeam.id, key: sdkTeam.key, name: sdkTeam.name } : null;
1190
+ }
1191
+
1192
+ async function fetchTeamStatesByQuery(client, teamId, options = {}) {
1193
+ const { first = 50 } = options;
1194
+
1195
+ if (getRawRequest(client)) {
1196
+ try {
1197
+ const data = await executeGraphQL(client, TEAM_STATES_QUERY, { id: teamId, first });
1198
+ if (data?.team) {
1199
+ return (data.team.states?.nodes || []).map((state) => ({
1200
+ id: state.id,
1201
+ name: state.name,
1202
+ type: state.type,
1203
+ }));
1204
+ }
1205
+ } catch {
1206
+ // Fall back to SDK read below.
1207
+ }
1208
+ }
1209
+
1210
+ const team = await client.team?.(teamId);
1211
+ if (!team) {
1212
+ return null;
1213
+ }
1214
+
1215
+ const result = await team.states();
1216
+ return (result.nodes || []).map((state) => ({
1217
+ id: state.id,
1218
+ name: state.name,
1219
+ type: state.type,
1220
+ }));
1221
+ }
1222
+
1223
+ async function fetchProjectMilestonesByQuery(client, projectId, options = {}) {
1224
+ const { first = 250 } = options;
1225
+
1226
+ if (!getRawRequest(client)) {
1227
+ const project = await client.project?.(projectId);
1228
+ if (!project) {
1229
+ return null;
1230
+ }
1231
+
1232
+ const result = await project.projectMilestones();
1233
+ const nodes = result.nodes || [];
1234
+ return Promise.all(nodes.map(transformMilestone));
1235
+ }
1236
+
1237
+ const data = await executeGraphQL(client, PROJECT_MILESTONES_QUERY, {
1238
+ id: projectId,
1239
+ first,
1240
+ });
1241
+
1242
+ if (!data?.project) {
1243
+ return null;
1244
+ }
1245
+
1246
+ const project = { id: data.project.id, name: data.project.name };
1247
+ return (data.project.projectMilestones?.nodes || []).map((milestone) => transformRawMilestone(milestone, project));
1248
+ }
1249
+
531
1250
  // ===== RATE LIMIT TRACKING =====
532
1251
 
533
1252
  /**
@@ -1084,6 +1803,52 @@ function resolveProjectMilestoneIdFromInput(milestones, milestoneInput) {
1084
1803
  throw new Error(`Milestone not found in project: ${target}`);
1085
1804
  }
1086
1805
 
1806
+ /**
1807
+ * Resolve a milestone reference (name or ID) to a milestone object with id and name.
1808
+ * Requires project context to search for milestones by name.
1809
+ * @param {LinearClient} client - Linear SDK client
1810
+ * @param {string} milestoneRef - Milestone name or ID
1811
+ * @param {string} projectId - Project ID to search within
1812
+ * @returns {Promise<{id: string, name: string}>}
1813
+ */
1814
+ export async function resolveMilestoneRef(client, milestoneRef, projectId) {
1815
+ const ref = String(milestoneRef || '').trim();
1816
+ if (!ref) {
1817
+ throw new Error('Missing milestone reference');
1818
+ }
1819
+
1820
+ // If it's already a Linear ID (UUID format with 16+ hex chars), try to fetch it directly
1821
+ if (/^[0-9a-fA-F-]{16,}$/.test(ref)) {
1822
+ try {
1823
+ const milestone = await client.projectMilestone(ref);
1824
+ if (milestone) {
1825
+ return { id: milestone.id, name: milestone.name };
1826
+ }
1827
+ } catch {
1828
+ // fall through to name lookup
1829
+ }
1830
+ throw new Error(`Milestone not found: ${ref}`);
1831
+ }
1832
+
1833
+ // Search by name in the project's milestones
1834
+ const milestones = await fetchProjectMilestones(client, projectId);
1835
+
1836
+ // Try exact name match first
1837
+ const exactMatch = milestones.find((m) => m.name === ref);
1838
+ if (exactMatch) {
1839
+ return { id: exactMatch.id, name: exactMatch.name };
1840
+ }
1841
+
1842
+ // Try case-insensitive match
1843
+ const lowerRef = ref.toLowerCase();
1844
+ const caseInsensitiveMatch = milestones.find((m) => m.name?.toLowerCase() === lowerRef);
1845
+ if (caseInsensitiveMatch) {
1846
+ return { id: caseInsensitiveMatch.id, name: caseInsensitiveMatch.name };
1847
+ }
1848
+
1849
+ throw new Error(`Milestone not found: ${ref}. Available milestones: ${milestones.map((m) => m.name).join(', ')}`);
1850
+ }
1851
+
1087
1852
  function normalizeIssueRefList(value) {
1088
1853
  if (value === undefined || value === null) return [];
1089
1854
  if (Array.isArray(value)) {
@@ -1368,17 +2133,18 @@ export async function resolveTeamRef(client, teamRef) {
1368
2133
  throw new Error('Missing team reference');
1369
2134
  }
1370
2135
 
1371
- // If it looks like a Linear ID (UUID), try direct lookup first (cheap)
2136
+ // If it looks like a Linear ID (UUID), try a minimal GraphQL lookup first.
1372
2137
  if (isLinearId(ref)) {
1373
2138
  try {
1374
- const direct = await client.team(ref);
2139
+ const direct = await fetchTeamMinimal(client, ref);
1375
2140
  if (direct) {
1376
- return { id: direct.id, key: direct.key, name: direct.name };
2141
+ return direct;
1377
2142
  }
1378
2143
  } catch {
1379
2144
  // fall back to cached/full-team lookup below
1380
2145
  }
1381
2146
 
2147
+ const teams = await fetchTeams(client);
1382
2148
  const byId = teams.find((t) => t.id === ref);
1383
2149
  if (byId) {
1384
2150
  return byId;
@@ -1422,14 +2188,13 @@ export async function resolveIssue(client, issueRef) {
1422
2188
  return withLinearErrorHandling(async () => {
1423
2189
  const lookup = normalizeIssueLookupInput(issueRef);
1424
2190
 
1425
- // The SDK's client.issue() method accepts both UUIDs and identifiers (ABC-123)
1426
2191
  try {
1427
- const issue = await client.issue(lookup);
2192
+ const issue = await fetchIssueMinimal(client, lookup);
1428
2193
  if (issue) {
1429
- return transformIssue(issue);
2194
+ return issue;
1430
2195
  }
1431
- } catch (err) {
1432
- // Fall through to error
2196
+ } catch {
2197
+ // Fall through to not-found error below
1433
2198
  }
1434
2199
 
1435
2200
  throw new Error(`Issue not found: ${lookup}`);
@@ -1448,18 +2213,11 @@ export async function getTeamWorkflowStates(client, teamRef) {
1448
2213
  const cached = getCache(teamStatesCache, cacheKey);
1449
2214
  if (cached) return cached;
1450
2215
 
1451
- const team = await client.team(teamRef);
1452
- if (!team) {
2216
+ const mapped = await fetchTeamStatesByQuery(client, teamRef);
2217
+ if (!mapped) {
1453
2218
  throw new Error(`Team not found: ${teamRef}`);
1454
2219
  }
1455
2220
 
1456
- const states = await team.states();
1457
- const mapped = (states.nodes || []).map(s => ({
1458
- id: s.id,
1459
- name: s.name,
1460
- type: s.type,
1461
- }));
1462
-
1463
2221
  setCache(teamStatesCache, cacheKey, mapped, CACHE_TTL_MS.teamStates);
1464
2222
  return mapped;
1465
2223
  }, 'getTeamWorkflowStates');
@@ -1479,12 +2237,12 @@ export async function resolveProjectRef(client, projectRef, options = {}) {
1479
2237
  throw new Error('Missing project reference');
1480
2238
  }
1481
2239
 
1482
- // If it looks like a Linear ID (UUID), try direct lookup first (cheap)
2240
+ // If it looks like a Linear ID (UUID), try a minimal GraphQL lookup first.
1483
2241
  if (isLinearId(ref)) {
1484
2242
  try {
1485
- const direct = await client.project(ref);
2243
+ const direct = await fetchProjectMinimal(client, ref);
1486
2244
  if (direct) {
1487
- return { id: direct.id, name: direct.name };
2245
+ return direct;
1488
2246
  }
1489
2247
  } catch {
1490
2248
  // fall back to cached/full-project lookup below
@@ -1925,109 +2683,127 @@ export async function updateDocument(client, documentRef, patch = {}) {
1925
2683
  * @param {boolean} [options.includeComments=true] - Include comments in response
1926
2684
  * @returns {Promise<Object>} Issue details
1927
2685
  */
1928
- export async function fetchIssueDetails(client, issueRef, options = {}) {
1929
- return withLinearErrorHandling(async () => {
1930
- const { includeComments = true } = options;
2686
+ async function fetchIssueDetailsViaSdk(client, issueRef, options = {}) {
2687
+ const { includeComments = true } = options;
1931
2688
 
1932
- // Resolve issue - client.issue() accepts both UUIDs and identifiers
1933
- const lookup = normalizeIssueLookupInput(issueRef);
1934
- const sdkIssue = await client.issue(lookup);
2689
+ const lookup = normalizeIssueLookupInput(issueRef);
2690
+ const sdkIssue = await client.issue(lookup);
1935
2691
 
1936
- if (!sdkIssue) {
1937
- throw new Error(`Issue not found: ${lookup}`);
1938
- }
2692
+ if (!sdkIssue) {
2693
+ throw new Error(`Issue not found: ${lookup}`);
2694
+ }
1939
2695
 
1940
- // Fetch all nested relations in parallel
1941
- const [
1942
- state,
1943
- team,
1944
- project,
1945
- projectMilestone,
1946
- assignee,
1947
- creator,
1948
- labelsResult,
1949
- parent,
1950
- childrenResult,
1951
- commentsResult,
1952
- attachmentsResult,
1953
- ] = await Promise.all([
1954
- sdkIssue.state?.catch?.(() => null) ?? sdkIssue.state,
1955
- sdkIssue.team?.catch?.(() => null) ?? sdkIssue.team,
1956
- sdkIssue.project?.catch?.(() => null) ?? sdkIssue.project,
1957
- sdkIssue.projectMilestone?.catch?.(() => null) ?? sdkIssue.projectMilestone,
1958
- sdkIssue.assignee?.catch?.(() => null) ?? sdkIssue.assignee,
1959
- sdkIssue.creator?.catch?.(() => null) ?? sdkIssue.creator,
1960
- sdkIssue.labels?.()?.catch?.(() => ({ nodes: [] })) ?? sdkIssue.labels?.() ?? { nodes: [] },
1961
- sdkIssue.parent?.catch?.(() => null) ?? sdkIssue.parent,
1962
- sdkIssue.children?.()?.catch?.(() => ({ nodes: [] })) ?? sdkIssue.children?.() ?? { nodes: [] },
1963
- includeComments ? (sdkIssue.comments?.()?.catch?.(() => ({ nodes: [] })) ?? sdkIssue.comments?.() ?? { nodes: [] }) : Promise.resolve({ nodes: [] }),
1964
- sdkIssue.attachments?.()?.catch?.(() => ({ nodes: [] })) ?? sdkIssue.attachments?.() ?? { nodes: [] },
1965
- ]);
2696
+ const [
2697
+ state,
2698
+ team,
2699
+ project,
2700
+ projectMilestone,
2701
+ assignee,
2702
+ creator,
2703
+ labelsResult,
2704
+ parent,
2705
+ childrenResult,
2706
+ commentsResult,
2707
+ attachmentsResult,
2708
+ ] = await Promise.all([
2709
+ sdkIssue.state?.catch?.(() => null) ?? sdkIssue.state,
2710
+ sdkIssue.team?.catch?.(() => null) ?? sdkIssue.team,
2711
+ sdkIssue.project?.catch?.(() => null) ?? sdkIssue.project,
2712
+ sdkIssue.projectMilestone?.catch?.(() => null) ?? sdkIssue.projectMilestone,
2713
+ sdkIssue.assignee?.catch?.(() => null) ?? sdkIssue.assignee,
2714
+ sdkIssue.creator?.catch?.(() => null) ?? sdkIssue.creator,
2715
+ sdkIssue.labels?.()?.catch?.(() => ({ nodes: [] })) ?? sdkIssue.labels?.() ?? { nodes: [] },
2716
+ sdkIssue.parent?.catch?.(() => null) ?? sdkIssue.parent,
2717
+ sdkIssue.children?.()?.catch?.(() => ({ nodes: [] })) ?? sdkIssue.children?.() ?? { nodes: [] },
2718
+ includeComments ? (sdkIssue.comments?.()?.catch?.(() => ({ nodes: [] })) ?? sdkIssue.comments?.() ?? { nodes: [] }) : Promise.resolve({ nodes: [] }),
2719
+ sdkIssue.attachments?.()?.catch?.(() => ({ nodes: [] })) ?? sdkIssue.attachments?.() ?? { nodes: [] },
2720
+ ]);
1966
2721
 
1967
- // Transform parent if exists
1968
- let transformedParent = null;
1969
- if (parent) {
1970
- const parentState = await parent.state?.catch?.(() => null) ?? parent.state;
1971
- transformedParent = {
1972
- identifier: parent.identifier,
1973
- title: parent.title,
1974
- state: parentState ? { name: parentState.name, color: parentState.color } : null,
1975
- };
1976
- }
2722
+ let transformedParent = null;
2723
+ if (parent) {
2724
+ const parentState = await parent.state?.catch?.(() => null) ?? parent.state;
2725
+ transformedParent = {
2726
+ identifier: parent.identifier,
2727
+ title: parent.title,
2728
+ state: parentState ? { name: parentState.name, color: parentState.color } : null,
2729
+ };
2730
+ }
1977
2731
 
1978
- const children = (childrenResult.nodes || []).map(c => ({
1979
- identifier: c.identifier,
1980
- title: c.title,
1981
- state: c.state ? { name: c.state.name, color: c.state.color } : null,
1982
- }));
2732
+ const children = (childrenResult.nodes || []).map(c => ({
2733
+ identifier: c.identifier,
2734
+ title: c.title,
2735
+ state: c.state ? { name: c.state.name, color: c.state.color } : null,
2736
+ }));
2737
+
2738
+ const comments = (commentsResult.nodes || []).map(c => ({
2739
+ id: c.id,
2740
+ body: c.body,
2741
+ createdAt: c.createdAt,
2742
+ updatedAt: c.updatedAt,
2743
+ user: c.user ? { name: c.user.name, displayName: c.user.displayName } : null,
2744
+ externalUser: c.externalUser ? { name: c.externalUser.name, displayName: c.externalUser.displayName } : null,
2745
+ parent: c.parent ? { id: c.parent.id } : null,
2746
+ }));
2747
+
2748
+ const attachments = (attachmentsResult.nodes || []).map(a => ({
2749
+ id: a.id,
2750
+ title: a.title,
2751
+ url: a.url,
2752
+ subtitle: a.subtitle,
2753
+ sourceType: a.sourceType,
2754
+ createdAt: a.createdAt,
2755
+ }));
2756
+
2757
+ const labels = (labelsResult.nodes || []).map(l => ({
2758
+ id: l.id,
2759
+ name: l.name,
2760
+ color: l.color,
2761
+ }));
1983
2762
 
1984
- const comments = (commentsResult.nodes || []).map(c => ({
1985
- id: c.id,
1986
- body: c.body,
1987
- createdAt: c.createdAt,
1988
- updatedAt: c.updatedAt,
1989
- user: c.user ? { name: c.user.name, displayName: c.user.displayName } : null,
1990
- externalUser: c.externalUser ? { name: c.externalUser.name, displayName: c.externalUser.displayName } : null,
1991
- parent: c.parent ? { id: c.parent.id } : null,
1992
- }));
2763
+ return {
2764
+ identifier: sdkIssue.identifier,
2765
+ title: sdkIssue.title,
2766
+ description: sdkIssue.description,
2767
+ url: sdkIssue.url,
2768
+ branchName: sdkIssue.branchName,
2769
+ priority: sdkIssue.priority,
2770
+ estimate: sdkIssue.estimate,
2771
+ createdAt: sdkIssue.createdAt,
2772
+ updatedAt: sdkIssue.updatedAt,
2773
+ state: state ? { name: state.name, color: state.color, type: state.type } : null,
2774
+ team: team ? { id: team.id, key: team.key, name: team.name } : null,
2775
+ project: project ? { id: project.id, name: project.name } : null,
2776
+ projectMilestone: projectMilestone ? { id: projectMilestone.id, name: projectMilestone.name } : null,
2777
+ assignee: assignee ? { id: assignee.id, name: assignee.name, displayName: assignee.displayName } : null,
2778
+ creator: creator ? { id: creator.id, name: creator.name, displayName: creator.displayName } : null,
2779
+ labels,
2780
+ parent: transformedParent,
2781
+ children,
2782
+ comments,
2783
+ attachments,
2784
+ };
2785
+ }
1993
2786
 
1994
- const attachments = (attachmentsResult.nodes || []).map(a => ({
1995
- id: a.id,
1996
- title: a.title,
1997
- url: a.url,
1998
- subtitle: a.subtitle,
1999
- sourceType: a.sourceType,
2000
- createdAt: a.createdAt,
2001
- }));
2787
+ export async function fetchIssueDetails(client, issueRef, options = {}) {
2788
+ return withLinearErrorHandling(async () => {
2789
+ const { includeComments = true } = options;
2002
2790
 
2003
- const labels = (labelsResult.nodes || []).map(l => ({
2004
- id: l.id,
2005
- name: l.name,
2006
- color: l.color,
2007
- }));
2791
+ if (!getRawRequest(client)) {
2792
+ return fetchIssueDetailsViaSdk(client, issueRef, options);
2793
+ }
2008
2794
 
2009
- return {
2010
- identifier: sdkIssue.identifier,
2011
- title: sdkIssue.title,
2012
- description: sdkIssue.description,
2013
- url: sdkIssue.url,
2014
- branchName: sdkIssue.branchName,
2015
- priority: sdkIssue.priority,
2016
- estimate: sdkIssue.estimate,
2017
- createdAt: sdkIssue.createdAt,
2018
- updatedAt: sdkIssue.updatedAt,
2019
- state: state ? { name: state.name, color: state.color, type: state.type } : null,
2020
- team: team ? { id: team.id, key: team.key, name: team.name } : null,
2021
- project: project ? { id: project.id, name: project.name } : null,
2022
- projectMilestone: projectMilestone ? { id: projectMilestone.id, name: projectMilestone.name } : null,
2023
- assignee: assignee ? { id: assignee.id, name: assignee.name, displayName: assignee.displayName } : null,
2024
- creator: creator ? { id: creator.id, name: creator.name, displayName: creator.displayName } : null,
2025
- labels,
2026
- parent: transformedParent,
2027
- children,
2028
- comments,
2029
- attachments,
2030
- };
2795
+ const lookup = normalizeIssueLookupInput(issueRef);
2796
+ const issueId = isLinearId(lookup)
2797
+ ? lookup
2798
+ : (await resolveIssue(client, lookup)).id;
2799
+ const query = includeComments ? ISSUE_DETAILS_WITH_COMMENTS_QUERY : ISSUE_DETAILS_QUERY;
2800
+ const data = await executeGraphQL(client, query, { id: issueId });
2801
+
2802
+ if (!data?.issue) {
2803
+ throw new Error(`Issue not found: ${lookup}`);
2804
+ }
2805
+
2806
+ return transformRawIssueDetails(data.issue, { includeComments });
2031
2807
  }, 'fetchIssueDetails');
2032
2808
  }
2033
2809
 
@@ -2115,17 +2891,17 @@ export async function fetchIssueActivity(client, issueRef, options = {}) {
2115
2891
  */
2116
2892
  export async function setIssueState(client, issueId, stateId) {
2117
2893
  return withLinearErrorHandling(async () => {
2118
- const issue = await client.issue(issueId);
2119
- if (!issue) {
2120
- throw new Error(`Issue not found: ${issueId}`);
2894
+ const updated = await performIssueUpdate(client, issueId, { stateId });
2895
+ if (updated) {
2896
+ return updated;
2121
2897
  }
2122
2898
 
2123
- const result = await issue.update({ stateId });
2124
- if (!result.success) {
2125
- throw new Error('Failed to update issue state');
2899
+ const refreshed = await fetchIssueMinimalById(client, issueId);
2900
+ if (!refreshed) {
2901
+ throw new Error(`Issue not found: ${issueId}`);
2126
2902
  }
2127
2903
 
2128
- return transformIssue(result.issue);
2904
+ return refreshed;
2129
2905
  }, 'setIssueState');
2130
2906
  }
2131
2907
 
@@ -2175,6 +2951,14 @@ export async function createIssue(client, input) {
2175
2951
  createInput.priority = parsed;
2176
2952
  }
2177
2953
 
2954
+ if (input.estimate !== undefined) {
2955
+ const parsed = Number.parseInt(String(input.estimate), 10);
2956
+ if (Number.isNaN(parsed) || parsed < 0) {
2957
+ throw new Error(`Invalid estimate: ${input.estimate}. Must be a non-negative integer.`);
2958
+ }
2959
+ createInput.estimate = parsed;
2960
+ }
2961
+
2178
2962
  if (input.assigneeId !== undefined) {
2179
2963
  createInput.assigneeId = input.assigneeId;
2180
2964
  }
@@ -2187,33 +2971,55 @@ export async function createIssue(client, input) {
2187
2971
  createInput.stateId = input.stateId;
2188
2972
  }
2189
2973
 
2190
- const result = await client.createIssue(createInput);
2974
+ if (getRawRequest(client)) {
2975
+ const payload = await executeGraphQL(client, ISSUE_CREATE_MUTATION, { input: createInput });
2976
+ if (!payload?.issueCreate?.success) {
2977
+ throw new Error('Failed to create issue');
2978
+ }
2191
2979
 
2192
- if (!result.success) {
2193
- throw new Error('Failed to create issue');
2194
- }
2980
+ const createdIssue = transformRawIssue(payload.issueCreate.issue ?? null);
2981
+ if (createdIssue) {
2982
+ return createdIssue;
2983
+ }
2984
+ } else {
2985
+ const result = await client.createIssue(createInput);
2195
2986
 
2196
- // Prefer official data path: resolve created issue ID then refetch full issue.
2197
- const createdIssueId =
2198
- result.issue?.id
2199
- || result._issue?.id
2200
- || null;
2987
+ if (!result.success) {
2988
+ throw new Error('Failed to create issue');
2989
+ }
2201
2990
 
2202
- if (createdIssueId) {
2203
- try {
2204
- const fullIssue = await client.issue(createdIssueId);
2205
- if (fullIssue) {
2206
- const transformed = await transformIssue(fullIssue);
2207
- return transformed;
2991
+ const createdIssueId =
2992
+ result.issue?.id
2993
+ || result._issue?.id
2994
+ || null;
2995
+
2996
+ if (createdIssueId) {
2997
+ try {
2998
+ const fullIssue = await fetchIssueMinimalById(client, createdIssueId);
2999
+ if (fullIssue) {
3000
+ return fullIssue;
3001
+ }
3002
+ } catch {
3003
+ // continue to fallback
2208
3004
  }
2209
- } catch {
2210
- // continue to fallback
2211
3005
  }
3006
+
3007
+ return {
3008
+ id: createdIssueId,
3009
+ identifier: null,
3010
+ title,
3011
+ description: input.description ?? null,
3012
+ url: null,
3013
+ priority: input.priority ?? null,
3014
+ state: null,
3015
+ team: null,
3016
+ project: null,
3017
+ assignee: null,
3018
+ };
2212
3019
  }
2213
3020
 
2214
- // Minimal fallback when SDK payload does not expose a resolvable issue ID.
2215
3021
  return {
2216
- id: createdIssueId,
3022
+ id: null,
2217
3023
  identifier: null,
2218
3024
  title,
2219
3025
  description: input.description ?? null,
@@ -2285,6 +3091,9 @@ function buildFallbackUpdatedIssue(targetIssue, updateInput) {
2285
3091
  if (Object.prototype.hasOwnProperty.call(updateInput, 'priority')) {
2286
3092
  fallback.priority = updateInput.priority;
2287
3093
  }
3094
+ if (Object.prototype.hasOwnProperty.call(updateInput, 'estimate')) {
3095
+ fallback.estimate = updateInput.estimate;
3096
+ }
2288
3097
 
2289
3098
  if (Object.prototype.hasOwnProperty.call(updateInput, 'assigneeId')) {
2290
3099
  const assigneeId = updateInput.assigneeId;
@@ -2355,6 +3164,15 @@ export async function updateIssue(client, issueRef, patch = {}) {
2355
3164
  updateInput.priority = parsed;
2356
3165
  }
2357
3166
 
3167
+
3168
+ if (patch.estimate !== undefined) {
3169
+ const parsed = Number.parseInt(String(patch.estimate), 10);
3170
+ if (Number.isNaN(parsed) || parsed < 0) {
3171
+ throw new Error(`Invalid estimate: ${patch.estimate}. Must be a non-negative integer.`);
3172
+ }
3173
+ updateInput.estimate = parsed;
3174
+ }
3175
+
2358
3176
  if (patch.state !== undefined) {
2359
3177
  // Need to resolve state ID from team's workflow states
2360
3178
  const team = targetIssue.team;
@@ -2409,14 +3227,7 @@ export async function updateIssue(client, issueRef, patch = {}) {
2409
3227
 
2410
3228
  for (const childRef of parentOfRefs) {
2411
3229
  const childIssue = await resolveIssue(client, childRef);
2412
- const childSdkIssue = await client.issue(childIssue.id);
2413
- if (!childSdkIssue) {
2414
- throw new Error(`Issue not found: ${childRef}`);
2415
- }
2416
- const rel = await childSdkIssue.update({ parentId: targetIssue.id });
2417
- if (!rel.success) {
2418
- throw new Error(`Failed to set parent for issue: ${childRef}`);
2419
- }
3230
+ await performIssueUpdate(client, childIssue.id, { parentId: targetIssue.id });
2420
3231
  }
2421
3232
 
2422
3233
  for (const blockerRef of blockedByRefs) {
@@ -2455,16 +3266,7 @@ export async function updateIssue(client, issueRef, patch = {}) {
2455
3266
  }
2456
3267
 
2457
3268
  if (Object.keys(updateInput).length > 0) {
2458
- // Get fresh issue instance for update
2459
- const sdkIssue = await client.issue(targetIssue.id);
2460
- if (!sdkIssue) {
2461
- throw new Error(`Issue not found: ${targetIssue.id}`);
2462
- }
2463
-
2464
- const result = await sdkIssue.update(updateInput);
2465
- if (!result.success) {
2466
- throw new Error('Failed to update issue');
2467
- }
3269
+ await performIssueUpdate(client, targetIssue.id, updateInput);
2468
3270
  }
2469
3271
 
2470
3272
  for (const relationInput of relationCreates) {
@@ -2479,8 +3281,7 @@ export async function updateIssue(client, issueRef, patch = {}) {
2479
3281
  let updatedIssue = null;
2480
3282
  let usedRateLimitFallback = false;
2481
3283
  try {
2482
- const updatedSdkIssue = await client.issue(targetIssue.id);
2483
- updatedIssue = await transformIssue(updatedSdkIssue);
3284
+ updatedIssue = await fetchIssueMinimalById(client, targetIssue.id);
2484
3285
  } catch (refreshError) {
2485
3286
  const refreshMessage = String(refreshError?.message || refreshError || 'unknown');
2486
3287
  const isRefreshRateLimited = refreshError?.type === 'Ratelimited' || refreshMessage.toLowerCase().includes('rate limit');
@@ -2581,7 +3382,7 @@ async function transformMilestone(sdkMilestone) {
2581
3382
  name: sdkMilestone.name,
2582
3383
  description: sdkMilestone.description,
2583
3384
  progress: sdkMilestone.progress,
2584
- order: sdkMilestone.order,
3385
+ order: sdkMilestone.sortOrder,
2585
3386
  targetDate: sdkMilestone.targetDate,
2586
3387
  status: sdkMilestone.status,
2587
3388
  project: project ? { id: project.id, name: project.name } : null,
@@ -2703,16 +3504,11 @@ function invalidateProjectsCache(client) {
2703
3504
  */
2704
3505
  export async function fetchProjectMilestones(client, projectId) {
2705
3506
  return withLinearErrorHandling(async () => {
2706
- const project = await client.project(projectId);
2707
- if (!project) {
3507
+ const milestones = await fetchProjectMilestonesByQuery(client, projectId);
3508
+ if (!milestones) {
2708
3509
  throw new Error(`Project not found: ${projectId}`);
2709
3510
  }
2710
3511
 
2711
- const result = await project.projectMilestones();
2712
- const nodes = result.nodes || [];
2713
-
2714
- const milestones = await Promise.all(nodes.map(transformMilestone));
2715
-
2716
3512
  debug('Fetched project milestones', {
2717
3513
  projectId,
2718
3514
  milestoneCount: milestones.length,
@@ -2766,7 +3562,7 @@ export async function fetchMilestoneDetails(client, milestoneId) {
2766
3562
  name: milestone.name,
2767
3563
  description: milestone.description,
2768
3564
  progress: milestone.progress,
2769
- order: milestone.order,
3565
+ order: milestone.sortOrder,
2770
3566
  targetDate: milestone.targetDate,
2771
3567
  status: milestone.status,
2772
3568
  project: project ? { id: project.id, name: project.name } : null,
@@ -2847,7 +3643,7 @@ export async function createProjectMilestone(client, input) {
2847
3643
  name: created?.name || name,
2848
3644
  description: created?.description ?? input.description ?? null,
2849
3645
  progress: created?.progress ?? 0,
2850
- order: created?.order ?? null,
3646
+ order: created?.sortOrder ?? null,
2851
3647
  targetDate: created?.targetDate ?? input.targetDate ?? null,
2852
3648
  status: created?.status ?? input.status ?? 'backlogged',
2853
3649
  project: null,
@@ -2944,16 +3740,21 @@ export async function deleteIssue(client, issueRef) {
2944
3740
  return withLinearErrorHandling(async () => {
2945
3741
  const targetIssue = await resolveIssue(client, issueRef);
2946
3742
 
2947
- // Get SDK issue instance for delete
2948
- const sdkIssue = await client.issue(targetIssue.id);
2949
- if (!sdkIssue) {
2950
- throw new Error(`Issue not found: ${targetIssue.id}`);
3743
+ let success = false;
3744
+ if (getRawRequest(client)) {
3745
+ const payload = await executeGraphQL(client, ISSUE_DELETE_MUTATION, { id: targetIssue.id });
3746
+ success = payload?.issueDelete?.success === true;
3747
+ } else {
3748
+ const sdkIssue = await client.issue(targetIssue.id);
3749
+ if (!sdkIssue) {
3750
+ throw new Error(`Issue not found: ${targetIssue.id}`);
3751
+ }
3752
+ const result = await sdkIssue.delete();
3753
+ success = result.success;
2951
3754
  }
2952
3755
 
2953
- const result = await sdkIssue.delete();
2954
-
2955
3756
  return {
2956
- success: result.success,
3757
+ success,
2957
3758
  issueId: targetIssue.id,
2958
3759
  identifier: targetIssue.identifier,
2959
3760
  };