@cliangdev/flux-plugin 0.3.1-dev.ee3b5ee → 0.4.0-dev.0892a21

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.
@@ -21,11 +21,11 @@ async function fetchCurrentFile(
21
21
  encoding: string;
22
22
  };
23
23
  return { sha: data.sha, content: data.content };
24
- } catch (err: any) {
25
- if (err.status === 404) {
24
+ } catch (error: unknown) {
25
+ if ((error as { status?: number }).status === 404) {
26
26
  return null;
27
27
  }
28
- throw err;
28
+ throw error;
29
29
  }
30
30
  }
31
31
 
@@ -63,13 +63,17 @@ export async function writeIndex(
63
63
  }
64
64
 
65
65
  try {
66
- await client.rest.repos.createOrUpdateFileContents(params as any);
67
- } catch (err: any) {
68
- if (err.status === 409) {
66
+ await client.rest.repos.createOrUpdateFileContents(
67
+ params as Parameters<
68
+ typeof client.rest.repos.createOrUpdateFileContents
69
+ >[0],
70
+ );
71
+ } catch (error: unknown) {
72
+ if ((error as { status?: number }).status === 409) {
69
73
  throw new Error(
70
74
  "Index conflict: another operation modified the index concurrently. Retry the operation.",
71
75
  );
72
76
  }
73
- throw err;
77
+ throw error;
74
78
  }
75
79
  }
@@ -10,6 +10,26 @@
10
10
  * - Task → Linear Issue child of Epic with "task" label
11
11
  */
12
12
 
13
+ import type {
14
+ Attachment,
15
+ Issue,
16
+ IssueRelation,
17
+ WorkflowState,
18
+ } from "@linear/sdk";
19
+ import { IssueRelationType } from "@linear/sdk";
20
+
21
+ interface RawIssue {
22
+ update(input: Record<string, unknown>): Promise<unknown>;
23
+ archive(): Promise<unknown>;
24
+ children(): Promise<{ nodes: Issue[] }>;
25
+ attachments(): Promise<{ nodes: ArchivableAttachment[] }>;
26
+ inverseRelations(): Promise<{ nodes: IssueRelation[] }>;
27
+ }
28
+
29
+ interface ArchivableAttachment extends Attachment {
30
+ archive(): Promise<unknown>;
31
+ }
32
+
13
33
  import type {
14
34
  AcceptanceCriterion,
15
35
  AddCriterionInput,
@@ -70,8 +90,7 @@ export interface HydratedIssue {
70
90
  priority: number;
71
91
  createdAt: Date;
72
92
  updatedAt: Date;
73
- // Raw issue for operations that need it (update, archive, etc.)
74
- _raw: any;
93
+ _raw: unknown;
75
94
  }
76
95
 
77
96
  export class LinearAdapter implements BackendAdapter {
@@ -87,6 +106,10 @@ export class LinearAdapter implements BackendAdapter {
87
106
  // Helper Methods
88
107
  // -------------------------------------------------------------------------
89
108
 
109
+ private raw(issue: HydratedIssue): RawIssue {
110
+ return issue._raw as RawIssue;
111
+ }
112
+
90
113
  private extractIssueNumber(identifier: string): number {
91
114
  const match = identifier.match(/-(\d+)$/);
92
115
  if (!match) {
@@ -98,12 +121,16 @@ export class LinearAdapter implements BackendAdapter {
98
121
  /**
99
122
  * Hydrate a raw Linear issue by resolving all lazy-loaded fields in parallel.
100
123
  */
101
- async hydrateIssue(issue: any): Promise<HydratedIssue> {
124
+ async hydrateIssue(issue: Issue): Promise<HydratedIssue> {
125
+ const statePromise = issue.state;
126
+ const parentPromise = issue.parent;
102
127
  const [state, labelsResult, parent] = await Promise.all([
103
- this.client.execute<any>(() => issue.state),
104
- this.client.execute<any>(() => issue.labels()),
105
- issue.parent
106
- ? this.client.execute<any>(() => issue.parent)
128
+ statePromise
129
+ ? this.client.execute<WorkflowState>(() => statePromise)
130
+ : Promise.resolve(undefined),
131
+ this.client.execute(() => issue.labels()),
132
+ parentPromise
133
+ ? this.client.execute<Issue>(() => parentPromise)
107
134
  : Promise.resolve(null),
108
135
  ]);
109
136
 
@@ -111,10 +138,10 @@ export class LinearAdapter implements BackendAdapter {
111
138
  id: issue.id,
112
139
  identifier: issue.identifier,
113
140
  title: issue.title,
114
- description: issue.description,
141
+ description: issue.description ?? undefined,
115
142
  stateName: state?.name || "Backlog",
116
143
  stateType: state?.type,
117
- labels: (labelsResult?.nodes || []).map((l: any) => l.name),
144
+ labels: (labelsResult?.nodes || []).map((l) => l.name),
118
145
  parentIdentifier: parent?.identifier,
119
146
  priority: issue.priority ?? 3,
120
147
  createdAt: issue.createdAt,
@@ -146,14 +173,15 @@ export class LinearAdapter implements BackendAdapter {
146
173
  /**
147
174
  * Fetch multiple issues with a filter and hydrate them.
148
175
  */
149
- async fetchIssues(filter: any, limit: number): Promise<HydratedIssue[]> {
176
+ async fetchIssues(
177
+ filter: Record<string, unknown>,
178
+ limit: number,
179
+ ): Promise<HydratedIssue[]> {
150
180
  const result = await this.client.execute(() =>
151
181
  this.client.client.issues({ filter, first: limit }),
152
182
  );
153
183
 
154
- return Promise.all(
155
- result.nodes.map((issue: any) => this.hydrateIssue(issue)),
156
- );
184
+ return Promise.all(result.nodes.map((issue) => this.hydrateIssue(issue)));
157
185
  }
158
186
 
159
187
  /**
@@ -185,7 +213,7 @@ export class LinearAdapter implements BackendAdapter {
185
213
  this.client.client.team(this.config.teamId),
186
214
  );
187
215
  const states = await this.client.execute(() => team.states());
188
- const targetState = states.nodes.find((s: any) => s.name === stateName);
216
+ const targetState = states.nodes.find((s) => s.name === stateName);
189
217
  if (!targetState) {
190
218
  throw new Error(`Workflow state '${stateName}' not found`);
191
219
  }
@@ -274,8 +302,10 @@ export class LinearAdapter implements BackendAdapter {
274
302
  }),
275
303
  );
276
304
 
277
- const rawIssue = await this.client.execute<any>(
278
- () => (createResult as any).issue,
305
+ const rawIssue = await this.client.execute<Issue | undefined>(
306
+ () =>
307
+ (createResult as unknown as { issue: Promise<Issue | undefined> })
308
+ .issue,
279
309
  );
280
310
  if (!rawIssue) {
281
311
  throw new Error("Failed to create PRD issue");
@@ -290,7 +320,7 @@ export class LinearAdapter implements BackendAdapter {
290
320
  if (!issue) throw new Error(`PRD not found: ${ref}`);
291
321
  if (!this.isPrd(issue)) throw new Error(`Issue ${ref} is not a PRD`);
292
322
 
293
- const updatePayload: any = {};
323
+ const updatePayload: Record<string, unknown> = {};
294
324
  if (input.title !== undefined) updatePayload.title = input.title;
295
325
  if (input.description !== undefined)
296
326
  updatePayload.description = input.description;
@@ -317,38 +347,13 @@ export class LinearAdapter implements BackendAdapter {
317
347
  );
318
348
  }
319
349
 
320
- await this.client.execute(() => issue._raw.update(updatePayload));
350
+ await this.client.execute(() => this.raw(issue).update(updatePayload));
321
351
 
322
352
  const updated = await this.fetchIssue(ref);
323
353
  if (!updated) throw new Error(`Failed to fetch updated PRD: ${ref}`);
324
354
  return this.toPrd(updated);
325
355
  }
326
356
 
327
- /**
328
- * Build label IDs for a PRD status update.
329
- * Keeps existing non-status labels, adds new status label if needed.
330
- * @deprecated Use buildPrdLabelIdsWithTag for updates that may include tag changes
331
- */
332
- private async buildPrdLabelIds(
333
- currentLabels: string[],
334
- newStatus: import("../types.js").PrdStatus,
335
- ): Promise<string[]> {
336
- const statusLabels = getAllStatusLabels();
337
- const newStatusLabel = getStatusLabelForPrdStatus(newStatus);
338
-
339
- const labelsToKeep = currentLabels.filter((l) => !statusLabels.includes(l));
340
- if (newStatusLabel) {
341
- labelsToKeep.push(newStatusLabel);
342
- }
343
-
344
- const labelIds: string[] = [];
345
- for (const labelName of labelsToKeep) {
346
- const id = await this.getOrCreateLabel(labelName);
347
- labelIds.push(id);
348
- }
349
- return labelIds;
350
- }
351
-
352
357
  /**
353
358
  * Build label IDs for a PRD update, handling both status and tag changes.
354
359
  * Filters out status labels and milestone labels, then adds the appropriate ones back.
@@ -393,14 +398,20 @@ export class LinearAdapter implements BackendAdapter {
393
398
  try {
394
399
  return await this.getLabelId(labelName);
395
400
  } catch {
396
- const result = await this.client.execute<any>(() =>
397
- (this.client.client as any).createIssueLabel({
401
+ const result = await this.client.execute(() =>
402
+ this.client.client.createIssueLabel({
398
403
  name: labelName,
399
404
  teamId: this.config.teamId,
400
405
  }),
401
406
  );
402
- // Linear SDK returns _issueLabel (with underscore prefix)
403
- const labelId = result?._issueLabel?.id ?? result?.issueLabel?.id;
407
+ const labelPayload = result as unknown as Record<string, unknown>;
408
+ const issueLabelField = labelPayload._issueLabel as
409
+ | { id: string }
410
+ | undefined;
411
+ const issueLabelGetter = labelPayload.issueLabel as
412
+ | { id: string }
413
+ | undefined;
414
+ const labelId = issueLabelField?.id ?? issueLabelGetter?.id;
404
415
  if (!labelId || typeof labelId !== "string") {
405
416
  throw new Error(
406
417
  `Failed to create label: ${labelName}. Result: ${JSON.stringify(result)}`,
@@ -423,11 +434,10 @@ export class LinearAdapter implements BackendAdapter {
423
434
  const limit = pagination?.limit ?? 50;
424
435
  const offset = pagination?.offset ?? 0;
425
436
 
426
- const linearFilter: any = {
437
+ const linearFilter: Record<string, unknown> = {
427
438
  project: { id: { eq: this.config.projectId } },
428
439
  };
429
440
 
430
- // Build label filter - always include prd label, optionally include tag label
431
441
  if (filters?.tag) {
432
442
  linearFilter.labels = {
433
443
  and: [
@@ -464,8 +474,8 @@ export class LinearAdapter implements BackendAdapter {
464
474
  const issue = await this.fetchIssue(ref);
465
475
  if (!issue) throw new Error(`PRD not found: ${ref}`);
466
476
 
467
- const childrenResult = await this.client.execute<any>(() =>
468
- issue._raw.children(),
477
+ const childrenResult = await this.client.execute(() =>
478
+ this.raw(issue).children(),
469
479
  );
470
480
  const children = childrenResult.nodes || [];
471
481
 
@@ -475,19 +485,17 @@ export class LinearAdapter implements BackendAdapter {
475
485
  for (const child of children) {
476
486
  const hydratedChild = await this.hydrateIssue(child);
477
487
  if (this.isEpic(hydratedChild)) {
478
- const epicChildren = await this.client.execute<any>(() =>
479
- child.children(),
480
- );
488
+ const epicChildren = await this.client.execute(() => child.children());
481
489
  for (const task of epicChildren.nodes || []) {
482
- await this.client.execute<any>(() => task.archive());
490
+ await this.client.execute(() => task.archive());
483
491
  taskCount++;
484
492
  }
485
- await this.client.execute<any>(() => child.archive());
493
+ await this.client.execute(() => child.archive());
486
494
  epicCount++;
487
495
  }
488
496
  }
489
497
 
490
- await this.client.execute<any>(() => issue._raw.archive());
498
+ await this.client.execute(() => this.raw(issue).archive());
491
499
 
492
500
  return {
493
501
  deleted: ref,
@@ -529,8 +537,10 @@ export class LinearAdapter implements BackendAdapter {
529
537
  }),
530
538
  );
531
539
 
532
- const rawIssue = await this.client.execute<any>(
533
- () => (createResult as any).issue,
540
+ const rawIssue = await this.client.execute<Issue | undefined>(
541
+ () =>
542
+ (createResult as unknown as { issue: Promise<Issue | undefined> })
543
+ .issue,
534
544
  );
535
545
  if (!rawIssue) throw new Error("Failed to create Epic issue");
536
546
 
@@ -542,7 +552,7 @@ export class LinearAdapter implements BackendAdapter {
542
552
  const issue = await this.fetchIssue(ref);
543
553
  if (!issue) throw new Error(`Epic not found: ${ref}`);
544
554
 
545
- const updatePayload: any = {};
555
+ const updatePayload: Record<string, unknown> = {};
546
556
  if (input.title !== undefined) updatePayload.title = input.title;
547
557
  if (input.description !== undefined)
548
558
  updatePayload.description = input.description;
@@ -552,8 +562,7 @@ export class LinearAdapter implements BackendAdapter {
552
562
  );
553
563
  }
554
564
 
555
- await this.client.execute(() => issue._raw.update(updatePayload));
556
- // Re-fetch to get the updated issue with all fields
565
+ await this.client.execute(() => this.raw(issue).update(updatePayload));
557
566
  const updated = await this.fetchIssue(ref);
558
567
  if (!updated) {
559
568
  throw new Error(`Failed to fetch updated Epic: ${ref}`);
@@ -574,7 +583,7 @@ export class LinearAdapter implements BackendAdapter {
574
583
  const limit = pagination?.limit ?? 50;
575
584
  const offset = pagination?.offset ?? 0;
576
585
 
577
- const linearFilter: any = {
586
+ const linearFilter: Record<string, unknown> = {
578
587
  labels: { name: { eq: this.config.defaultLabels.epic } },
579
588
  project: { id: { eq: this.config.projectId } },
580
589
  };
@@ -609,16 +618,16 @@ export class LinearAdapter implements BackendAdapter {
609
618
  const issue = await this.fetchIssue(ref);
610
619
  if (!issue) throw new Error(`Epic not found: ${ref}`);
611
620
 
612
- const childrenResult = await this.client.execute<any>(() =>
613
- issue._raw.children(),
621
+ const childrenResult = await this.client.execute(() =>
622
+ this.raw(issue).children(),
614
623
  );
615
624
  const children = childrenResult.nodes || [];
616
625
 
617
626
  for (const child of children) {
618
- await this.client.execute<any>(() => child.archive());
627
+ await this.client.execute(() => child.archive());
619
628
  }
620
629
 
621
- await this.client.execute<any>(() => issue._raw.archive());
630
+ await this.client.execute(() => this.raw(issue).archive());
622
631
 
623
632
  return {
624
633
  deleted: ref,
@@ -659,8 +668,10 @@ export class LinearAdapter implements BackendAdapter {
659
668
  }),
660
669
  );
661
670
 
662
- const rawIssue = await this.client.execute<any>(
663
- () => (createResult as any).issue,
671
+ const rawIssue = await this.client.execute<Issue | undefined>(
672
+ () =>
673
+ (createResult as unknown as { issue: Promise<Issue | undefined> })
674
+ .issue,
664
675
  );
665
676
  if (!rawIssue) throw new Error("Failed to create Task issue");
666
677
 
@@ -672,7 +683,7 @@ export class LinearAdapter implements BackendAdapter {
672
683
  const issue = await this.fetchIssue(ref);
673
684
  if (!issue) throw new Error(`Task not found: ${ref}`);
674
685
 
675
- const updatePayload: any = {};
686
+ const updatePayload: Record<string, unknown> = {};
676
687
  if (input.title !== undefined) updatePayload.title = input.title;
677
688
  if (input.description !== undefined)
678
689
  updatePayload.description = input.description;
@@ -684,11 +695,11 @@ export class LinearAdapter implements BackendAdapter {
684
695
  this.client.client.team(this.config.teamId),
685
696
  );
686
697
  const states = await this.client.execute(() => team.states());
687
- const targetState = states.nodes.find((s: any) => s.name === stateName);
698
+ const targetState = states.nodes.find((s) => s.name === stateName);
688
699
  if (targetState) updatePayload.stateId = targetState.id;
689
700
  }
690
701
 
691
- await this.client.execute(() => issue._raw.update(updatePayload));
702
+ await this.client.execute(() => this.raw(issue).update(updatePayload));
692
703
  // Re-fetch to get the updated issue with all fields
693
704
  const updated = await this.fetchIssue(ref);
694
705
  if (!updated) {
@@ -712,7 +723,7 @@ export class LinearAdapter implements BackendAdapter {
712
723
  const limit = pagination?.limit ?? 50;
713
724
  const offset = pagination?.offset ?? 0;
714
725
 
715
- const linearFilter: any = {
726
+ const linearFilter: Record<string, unknown> = {
716
727
  labels: { name: { eq: this.config.defaultLabels.task } },
717
728
  project: { id: { eq: this.config.projectId } },
718
729
  };
@@ -731,7 +742,7 @@ export class LinearAdapter implements BackendAdapter {
731
742
  );
732
743
 
733
744
  const issues = await Promise.all(
734
- result.nodes.map((i: any) => this.hydrateIssue(i)),
745
+ result.nodes.map((i) => this.hydrateIssue(i)),
735
746
  );
736
747
  const paginated = issues.slice(offset, offset + limit);
737
748
 
@@ -750,7 +761,7 @@ export class LinearAdapter implements BackendAdapter {
750
761
  const issue = await this.fetchIssue(ref);
751
762
  if (!issue) throw new Error(`Task not found: ${ref}`);
752
763
 
753
- await this.client.execute(() => issue._raw.archive());
764
+ await this.client.execute(() => this.raw(issue).archive());
754
765
 
755
766
  return {
756
767
  deleted: ref,
@@ -774,8 +785,8 @@ export class LinearAdapter implements BackendAdapter {
774
785
  input.criteria,
775
786
  );
776
787
 
777
- await this.client.execute<any>(() =>
778
- issue._raw.update({ description: newDescription }),
788
+ await this.client.execute(() =>
789
+ this.raw(issue).update({ description: newDescription }),
779
790
  );
780
791
 
781
792
  return {
@@ -808,8 +819,8 @@ export class LinearAdapter implements BackendAdapter {
808
819
  true,
809
820
  );
810
821
 
811
- await this.client.execute<any>(() =>
812
- issue._raw.update({ description: newDescription }),
822
+ await this.client.execute(() =>
823
+ this.raw(issue).update({ description: newDescription }),
813
824
  );
814
825
 
815
826
  return {
@@ -869,11 +880,11 @@ export class LinearAdapter implements BackendAdapter {
869
880
  );
870
881
  }
871
882
 
872
- await this.client.execute<any>(() =>
883
+ await this.client.execute(() =>
873
884
  this.client.client.createIssueRelation({
874
885
  issueId: blockerIssue.id,
875
886
  relatedIssueId: blockedIssue.id,
876
- type: "blocks" as any,
887
+ type: IssueRelationType.Blocks,
877
888
  }),
878
889
  );
879
890
  }
@@ -887,13 +898,11 @@ export class LinearAdapter implements BackendAdapter {
887
898
  if (!blockedIssue) throw new Error(`Issue not found: ${ref}`);
888
899
  if (!blockerIssue) throw new Error(`Issue not found: ${dependsOnRef}`);
889
900
 
890
- // Use inverseRelations to find relations where blockedIssue is the target
891
- const relations = await this.client.execute<any>(() =>
892
- blockedIssue._raw.inverseRelations(),
901
+ const relations = await this.client.execute(() =>
902
+ this.raw(blockedIssue).inverseRelations(),
893
903
  );
894
904
 
895
- // Find the relation to delete - need to await rel.issue since it's lazy-loaded
896
- let relationToDelete: any = null;
905
+ let relationToDelete: IssueRelation | null = null;
897
906
  for (const rel of relations.nodes) {
898
907
  if (rel.type === "blocks") {
899
908
  const relIssue = await rel.issue;
@@ -910,17 +919,15 @@ export class LinearAdapter implements BackendAdapter {
910
919
  );
911
920
  }
912
921
 
913
- await this.client.execute<any>(() => relationToDelete.delete());
922
+ await this.client.execute(() => relationToDelete.delete());
914
923
  }
915
924
 
916
925
  async getDependencies(ref: string): Promise<string[]> {
917
926
  const issue = await this.fetchIssue(ref);
918
927
  if (!issue) throw new Error(`Issue not found: ${ref}`);
919
928
 
920
- // Use inverseRelations to get relations where this issue is the target (relatedIssueId)
921
- // This finds issues that block this one
922
- const relations = await this.client.execute<any>(() =>
923
- issue._raw.inverseRelations(),
929
+ const relations = await this.client.execute(() =>
930
+ this.raw(issue).inverseRelations(),
924
931
  );
925
932
 
926
933
  const blockingRefs: string[] = [];
@@ -952,21 +959,32 @@ export class LinearAdapter implements BackendAdapter {
952
959
  const issue = await this.fetchIssue(doc.prdRef);
953
960
  if (!issue) throw new Error(`PRD ${doc.prdRef} not found`);
954
961
 
955
- const existingAttachments = await this.client.execute<any>(() =>
956
- issue._raw.attachments(),
962
+ const existingAttachments = await this.client.execute(() =>
963
+ this.raw(issue).attachments(),
957
964
  );
958
965
  const existingAttachment = existingAttachments.nodes.find(
959
- (att: any) => att.title === doc.filename,
966
+ (att) => att.title === doc.filename,
960
967
  );
961
968
 
962
969
  if (existingAttachment) {
963
- await this.client.execute<any>(() => existingAttachment.archive());
970
+ await this.client.execute(() => existingAttachment.archive());
964
971
  }
965
972
 
966
973
  const contentUrl = `data:text/markdown;base64,${Buffer.from(doc.content).toString("base64")}`;
967
974
 
968
- const attachResult = await this.client.execute<any>(() =>
969
- (this.client.client as any).attachmentCreate({
975
+ const attachResult = await this.client.execute<{
976
+ success: boolean;
977
+ attachment?: { url: string };
978
+ }>(() =>
979
+ (
980
+ this.client.client as unknown as {
981
+ attachmentCreate(input: {
982
+ title: string;
983
+ url: string;
984
+ issueId: string;
985
+ }): Promise<{ success: boolean; attachment?: { url: string } }>;
986
+ }
987
+ ).attachmentCreate({
970
988
  title: doc.filename,
971
989
  url: contentUrl,
972
990
  issueId: issue.id,
@@ -989,8 +1007,8 @@ export class LinearAdapter implements BackendAdapter {
989
1007
  const issue = await this.fetchIssue(prdRef);
990
1008
  if (!issue) throw new Error(`PRD ${prdRef} not found`);
991
1009
 
992
- const attachments = await this.client.execute<any>(() =>
993
- issue._raw.attachments(),
1010
+ const attachments = await this.client.execute(() =>
1011
+ this.raw(issue).attachments(),
994
1012
  );
995
1013
  const documents: Document[] = [];
996
1014
 
@@ -1021,16 +1039,14 @@ export class LinearAdapter implements BackendAdapter {
1021
1039
  const issue = await this.fetchIssue(prdRef);
1022
1040
  if (!issue) throw new Error(`PRD ${prdRef} not found`);
1023
1041
 
1024
- const attachments = await this.client.execute<any>(() =>
1025
- issue._raw.attachments(),
1026
- );
1027
- const attachment = attachments.nodes.find(
1028
- (att: any) => att.title === filename,
1042
+ const attachments = await this.client.execute(() =>
1043
+ this.raw(issue).attachments(),
1029
1044
  );
1045
+ const attachment = attachments.nodes.find((att) => att.title === filename);
1030
1046
 
1031
1047
  if (!attachment) throw new Error(`Document ${filename} not found`);
1032
1048
 
1033
- await this.client.execute<any>(() => attachment.archive());
1049
+ await this.client.execute(() => attachment.archive());
1034
1050
  }
1035
1051
 
1036
1052
  // -------------------------------------------------------------------------
@@ -73,13 +73,20 @@ export class LinearClient {
73
73
  for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
74
74
  try {
75
75
  return await operation();
76
- } catch (error: any) {
77
- lastError = error;
76
+ } catch (error: unknown) {
77
+ const err = error as {
78
+ status?: number;
79
+ message?: string;
80
+ code?: string;
81
+ };
82
+ const asError =
83
+ error instanceof Error ? error : new Error(String(error));
84
+ lastError = asError;
78
85
 
79
86
  // Check if error is retryable
80
- const isRateLimited = error.status === 429;
81
- const isNetworkError = this.isNetworkError(error);
82
- const isUnauthorized = error.status === 401;
87
+ const isRateLimited = err.status === 429;
88
+ const isNetworkError = this.isNetworkError(err);
89
+ const isUnauthorized = err.status === 401;
83
90
 
84
91
  // Throw immediately for unauthorized errors
85
92
  if (isUnauthorized) {
@@ -87,17 +94,17 @@ export class LinearClient {
87
94
  "Unauthorized: Invalid API key",
88
95
  "UNAUTHORIZED",
89
96
  401,
90
- error,
97
+ asError,
91
98
  );
92
99
  }
93
100
 
94
101
  // Throw immediately for non-retryable errors
95
102
  if (!isRateLimited && !isNetworkError) {
96
103
  throw new LinearApiError(
97
- error.message || "Linear API error",
104
+ err.message || "Linear API error",
98
105
  "API_ERROR",
99
- error.status,
100
- error,
106
+ err.status,
107
+ asError,
101
108
  );
102
109
  }
103
110
 
@@ -108,14 +115,14 @@ export class LinearClient {
108
115
  `Rate limited after ${this.maxRetries} retries`,
109
116
  "RATE_LIMITED",
110
117
  429,
111
- error,
118
+ asError,
112
119
  );
113
120
  }
114
121
  throw new LinearApiError(
115
- `Network error after ${this.maxRetries} retries: ${error.message}`,
122
+ `Network error after ${this.maxRetries} retries: ${asError.message}`,
116
123
  "NETWORK_ERROR",
117
124
  undefined,
118
- error,
125
+ asError,
119
126
  );
120
127
  }
121
128
 
@@ -137,7 +144,7 @@ export class LinearClient {
137
144
  /**
138
145
  * Check if an error is a network error.
139
146
  */
140
- private isNetworkError(error: any): boolean {
147
+ private isNetworkError(error: { code?: string; message?: string }): boolean {
141
148
  // Common network error codes
142
149
  const networkErrorCodes = [
143
150
  "ECONNRESET",
@@ -147,7 +154,7 @@ export class LinearClient {
147
154
  "ENETUNREACH",
148
155
  ];
149
156
 
150
- return networkErrorCodes.includes(error.code);
157
+ return error.code !== undefined && networkErrorCodes.includes(error.code);
151
158
  }
152
159
 
153
160
  /**