@alasano/pi-linear 0.1.1 → 0.3.0

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.
Files changed (38) hide show
  1. package/README.md +16 -2
  2. package/assets/linear_list_issues.png +0 -0
  3. package/assets/screenshot.png +0 -0
  4. package/extensions/params.ts +40 -1
  5. package/extensions/renderers/comments.ts +323 -0
  6. package/extensions/renderers/common.ts +305 -0
  7. package/extensions/renderers/documents.ts +326 -0
  8. package/extensions/renderers/initiatives.ts +344 -0
  9. package/extensions/renderers/issue-labels.ts +294 -0
  10. package/extensions/renderers/issue-relations.ts +318 -0
  11. package/extensions/renderers/issue-statuses.ts +199 -0
  12. package/extensions/renderers/issues.ts +373 -0
  13. package/extensions/renderers/milestones.ts +294 -0
  14. package/extensions/renderers/project-labels.ts +279 -0
  15. package/extensions/renderers/project-relations.ts +344 -0
  16. package/extensions/renderers/projects.ts +437 -0
  17. package/extensions/renderers/state.ts +35 -0
  18. package/extensions/renderers/teams.ts +246 -0
  19. package/extensions/renderers/users.ts +242 -0
  20. package/extensions/renderers/workspaces.ts +44 -0
  21. package/extensions/selections.ts +10 -3
  22. package/extensions/settings.ts +40 -7
  23. package/extensions/tools/comments.ts +30 -11
  24. package/extensions/tools/documents.ts +42 -11
  25. package/extensions/tools/initiatives.ts +43 -11
  26. package/extensions/tools/issue-labels.ts +36 -11
  27. package/extensions/tools/issue-relations.ts +32 -13
  28. package/extensions/tools/issue-statuses.ts +19 -11
  29. package/extensions/tools/issues.ts +53 -19
  30. package/extensions/tools/milestones.ts +31 -11
  31. package/extensions/tools/project-labels.ts +30 -11
  32. package/extensions/tools/project-relations.ts +32 -13
  33. package/extensions/tools/projects.ts +48 -16
  34. package/extensions/tools/teams.ts +23 -11
  35. package/extensions/tools/users.ts +23 -11
  36. package/extensions/tools/workspaces.ts +6 -0
  37. package/extensions/types.ts +12 -0
  38. package/package.json +1 -1
@@ -1,10 +1,14 @@
1
1
  import { defineTool } from '@mariozechner/pi-coding-agent';
2
2
  import { Type } from '@sinclair/typebox';
3
3
  import { withLinearAuth, linearGraphQL } from '../client';
4
- import { PaginationParams, FilterParam } from '../params';
4
+ import { PaginationParams, paginationVariables, FilterParam } from '../params';
5
5
  import { WORKFLOW_STATE_SELECTION } from '../selections';
6
- import type { JsonObject } from '../types';
6
+ import type { JsonObject, LinearConnection } from '../types';
7
7
  import { compactObject, asObject } from '../util';
8
+ import {
9
+ renderLinearIssueStatusListCall,
10
+ renderLinearIssueStatusListResult,
11
+ } from '../renderers/issue-statuses';
8
12
 
9
13
  export function issueStatusTools() {
10
14
  return [
@@ -17,20 +21,16 @@ export function issueStatusTools() {
17
21
  ...PaginationParams,
18
22
  ...FilterParam,
19
23
  }),
24
+ renderCall: renderLinearIssueStatusListCall,
20
25
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
21
26
  return withLinearAuth(ctx, signal, async (apiKey) => {
22
27
  const variables = compactObject({
23
- after: params.after,
24
- before: params.before,
28
+ ...paginationVariables(params, 50),
25
29
  filter: asObject(params.filter),
26
- first: params.first ?? 50,
27
- includeArchived: params.includeArchived,
28
- last: params.last,
29
- orderBy: params.orderBy,
30
30
  });
31
31
 
32
32
  const data = await linearGraphQL<{
33
- workflowStates: { nodes: Array<JsonObject> };
33
+ workflowStates: LinearConnection<JsonObject>;
34
34
  }>(
35
35
  apiKey,
36
36
  `query ListIssueStatuses(
@@ -54,6 +54,12 @@ export function issueStatusTools() {
54
54
  nodes {
55
55
  ${WORKFLOW_STATE_SELECTION}
56
56
  }
57
+ pageInfo {
58
+ hasNextPage
59
+ hasPreviousPage
60
+ startCursor
61
+ endCursor
62
+ }
57
63
  }
58
64
  }`,
59
65
  variables,
@@ -61,12 +67,14 @@ export function issueStatusTools() {
61
67
  );
62
68
 
63
69
  const states = data.workflowStates.nodes;
70
+ const pageInfo = data.workflowStates.pageInfo;
64
71
  return {
65
- content: [{ type: 'text', text: JSON.stringify({ states }, null, 2) }],
66
- details: { states },
72
+ content: [{ type: 'text', text: JSON.stringify({ states, pageInfo }, null, 2) }],
73
+ details: { states, pageInfo },
67
74
  };
68
75
  });
69
76
  },
77
+ renderResult: renderLinearIssueStatusListResult,
70
78
  }),
71
79
  ];
72
80
  }
@@ -9,14 +9,28 @@ import {
9
9
  } from '../client';
10
10
  import {
11
11
  PaginationParams,
12
+ paginationVariables,
12
13
  FilterParam,
13
14
  SortParam,
14
15
  TeamConvenienceParams,
15
16
  RawInputParam,
16
17
  } from '../params';
17
18
  import { ISSUE_SELECTION } from '../selections';
18
- import type { LinearIssue, JsonObject } from '../types';
19
+ import type { LinearIssue, JsonObject, LinearConnection } from '../types';
19
20
  import { compactObject, asObject, asObjectArray, asString, mergeFilters } from '../util';
21
+ import {
22
+ renderLinearArchiveIssueCall,
23
+ renderLinearCreateIssueCall,
24
+ renderLinearDeleteIssueCall,
25
+ renderLinearGetIssueCall,
26
+ renderLinearIssueListCall,
27
+ renderLinearIssueListResult,
28
+ renderLinearIssueResult,
29
+ renderLinearIssueSearchCall,
30
+ renderLinearIssueSuccessResult,
31
+ renderLinearUnarchiveIssueCall,
32
+ renderLinearUpdateIssueCall,
33
+ } from '../renderers/issues';
20
34
 
21
35
  export function issueTools() {
22
36
  return [
@@ -46,6 +60,7 @@ export function issueTools() {
46
60
  ...FilterParam,
47
61
  ...SortParam,
48
62
  }),
63
+ renderCall: renderLinearIssueListCall,
49
64
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
50
65
  return withLinearAuth(ctx, signal, async (apiKey) => {
51
66
  const convenienceFilter = compactObject({
@@ -65,17 +80,12 @@ export function issueTools() {
65
80
  );
66
81
 
67
82
  const variables = compactObject({
68
- after: params.after,
69
- before: params.before,
83
+ ...paginationVariables(params, 20),
70
84
  filter,
71
- first: params.first ?? 20,
72
- includeArchived: params.includeArchived,
73
- last: params.last,
74
- orderBy: params.orderBy,
75
85
  sort: asObjectArray(params.sort),
76
86
  });
77
87
 
78
- const data = await linearGraphQL<{ issues: { nodes: LinearIssue[] } }>(
88
+ const data = await linearGraphQL<{ issues: LinearConnection<LinearIssue> }>(
79
89
  apiKey,
80
90
  `query ListIssues(
81
91
  $after: String
@@ -100,6 +110,12 @@ export function issueTools() {
100
110
  nodes {
101
111
  ${ISSUE_SELECTION}
102
112
  }
113
+ pageInfo {
114
+ hasNextPage
115
+ hasPreviousPage
116
+ startCursor
117
+ endCursor
118
+ }
103
119
  }
104
120
  }`,
105
121
  variables,
@@ -107,12 +123,14 @@ export function issueTools() {
107
123
  );
108
124
 
109
125
  const issues = data.issues.nodes;
126
+ const pageInfo = data.issues.pageInfo;
110
127
  return {
111
- content: [{ type: 'text', text: JSON.stringify({ issues }, null, 2) }],
112
- details: { issues },
128
+ content: [{ type: 'text', text: JSON.stringify({ issues, pageInfo }, null, 2) }],
129
+ details: { issues, pageInfo },
113
130
  };
114
131
  });
115
132
  },
133
+ renderResult: renderLinearIssueListResult,
116
134
  }),
117
135
  defineTool({
118
136
  name: 'linear_get_issue',
@@ -123,6 +141,7 @@ export function issueTools() {
123
141
  description: 'Issue identifier (ENG-123) or issue id.',
124
142
  }),
125
143
  }),
144
+ renderCall: renderLinearGetIssueCall,
126
145
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
127
146
  return withLinearAuth(ctx, signal, async (apiKey) => {
128
147
  const issueRef = params.issue.trim();
@@ -149,6 +168,7 @@ export function issueTools() {
149
168
  };
150
169
  });
151
170
  },
171
+ renderResult: renderLinearIssueResult('Issue'),
152
172
  }),
153
173
  defineTool({
154
174
  name: 'linear_create_issue',
@@ -232,6 +252,7 @@ export function issueTools() {
232
252
  ),
233
253
  ...RawInputParam,
234
254
  }),
255
+ renderCall: renderLinearCreateIssueCall,
235
256
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
236
257
  return withLinearAuth(ctx, signal, async (apiKey) => {
237
258
  const rawInput = asObject(params.input) || {};
@@ -320,6 +341,7 @@ export function issueTools() {
320
341
  };
321
342
  });
322
343
  },
344
+ renderResult: renderLinearIssueResult('Created issue'),
323
345
  }),
324
346
  defineTool({
325
347
  name: 'linear_update_issue',
@@ -404,6 +426,7 @@ export function issueTools() {
404
426
  trashed: Type.Optional(Type.Boolean({ description: 'IssueUpdateInput.trashed' })),
405
427
  ...RawInputParam,
406
428
  }),
429
+ renderCall: renderLinearUpdateIssueCall,
407
430
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
408
431
  return withLinearAuth(ctx, signal, async (apiKey) => {
409
432
  const issueId = await resolveIssueId(apiKey, params.issue, signal);
@@ -484,6 +507,7 @@ export function issueTools() {
484
507
  };
485
508
  });
486
509
  },
510
+ renderResult: renderLinearIssueResult('Updated issue'),
487
511
  }),
488
512
  defineTool({
489
513
  name: 'linear_delete_issue',
@@ -495,6 +519,7 @@ export function issueTools() {
495
519
  }),
496
520
  permanentlyDelete: Type.Optional(Type.Boolean()),
497
521
  }),
522
+ renderCall: renderLinearDeleteIssueCall,
498
523
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
499
524
  return withLinearAuth(ctx, signal, async (apiKey) => {
500
525
  const issueId = await resolveIssueId(apiKey, params.issue, signal);
@@ -522,6 +547,7 @@ export function issueTools() {
522
547
  };
523
548
  });
524
549
  },
550
+ renderResult: renderLinearIssueSuccessResult('Deleted'),
525
551
  }),
526
552
  defineTool({
527
553
  name: 'linear_archive_issue',
@@ -534,6 +560,7 @@ export function issueTools() {
534
560
  }),
535
561
  trash: Type.Optional(Type.Boolean()),
536
562
  }),
563
+ renderCall: renderLinearArchiveIssueCall,
537
564
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
538
565
  return withLinearAuth(ctx, signal, async (apiKey) => {
539
566
  const issueId = await resolveIssueId(apiKey, params.issue, signal);
@@ -561,6 +588,7 @@ export function issueTools() {
561
588
  };
562
589
  });
563
590
  },
591
+ renderResult: renderLinearIssueSuccessResult('Archived'),
564
592
  }),
565
593
  defineTool({
566
594
  name: 'linear_unarchive_issue',
@@ -571,6 +599,7 @@ export function issueTools() {
571
599
  description: 'Issue identifier (ENG-123) or issue id.',
572
600
  }),
573
601
  }),
602
+ renderCall: renderLinearUnarchiveIssueCall,
574
603
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
575
604
  return withLinearAuth(ctx, signal, async (apiKey) => {
576
605
  const issueId = await resolveIssueId(apiKey, params.issue, signal);
@@ -598,6 +627,7 @@ export function issueTools() {
598
627
  };
599
628
  });
600
629
  },
630
+ renderResult: renderLinearIssueSuccessResult('Unarchived'),
601
631
  }),
602
632
  defineTool({
603
633
  name: 'linear_search_issues',
@@ -610,23 +640,19 @@ export function issueTools() {
610
640
  ...PaginationParams,
611
641
  ...FilterParam,
612
642
  }),
643
+ renderCall: renderLinearIssueSearchCall,
613
644
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
614
645
  return withLinearAuth(ctx, signal, async (apiKey) => {
615
646
  const variables = compactObject({
647
+ ...paginationVariables(params, 20),
616
648
  term: params.term,
617
649
  includeComments: params.includeComments,
618
650
  teamId: params.teamId,
619
- after: params.after,
620
- before: params.before,
621
651
  filter: asObject(params.filter),
622
- first: params.first ?? 20,
623
- includeArchived: params.includeArchived,
624
- last: params.last,
625
- orderBy: params.orderBy,
626
652
  });
627
653
 
628
654
  const data = await linearGraphQL<{
629
- searchIssues: { nodes: LinearIssue[] };
655
+ searchIssues: LinearConnection<LinearIssue>;
630
656
  }>(
631
657
  apiKey,
632
658
  `query SearchIssues(
@@ -656,6 +682,12 @@ export function issueTools() {
656
682
  nodes {
657
683
  ${ISSUE_SELECTION}
658
684
  }
685
+ pageInfo {
686
+ hasNextPage
687
+ hasPreviousPage
688
+ startCursor
689
+ endCursor
690
+ }
659
691
  }
660
692
  }`,
661
693
  variables,
@@ -663,12 +695,14 @@ export function issueTools() {
663
695
  );
664
696
 
665
697
  const issues = data.searchIssues.nodes;
698
+ const pageInfo = data.searchIssues.pageInfo;
666
699
  return {
667
- content: [{ type: 'text', text: JSON.stringify({ issues }, null, 2) }],
668
- details: { issues },
700
+ content: [{ type: 'text', text: JSON.stringify({ issues, pageInfo }, null, 2) }],
701
+ details: { issues, pageInfo },
669
702
  };
670
703
  });
671
704
  },
705
+ renderResult: renderLinearIssueListResult,
672
706
  }),
673
707
  ];
674
708
  }
@@ -1,10 +1,20 @@
1
1
  import { defineTool } from '@mariozechner/pi-coding-agent';
2
2
  import { Type } from '@sinclair/typebox';
3
3
  import { withLinearAuth, linearGraphQL } from '../client';
4
- import { PaginationParams, FilterParam, RawInputParam } from '../params';
4
+ import { PaginationParams, paginationVariables, FilterParam, RawInputParam } from '../params';
5
5
  import { MILESTONE_SELECTION } from '../selections';
6
- import type { JsonObject } from '../types';
6
+ import type { JsonObject, LinearConnection } from '../types';
7
7
  import { compactObject, asObject, asString, GenericObjectSchema } from '../util';
8
+ import {
9
+ renderLinearMilestoneDeleteCall,
10
+ renderLinearMilestoneDeleteResult,
11
+ renderLinearMilestoneGetCall,
12
+ renderLinearMilestoneListCall,
13
+ renderLinearMilestoneListResult,
14
+ renderLinearMilestoneResult,
15
+ renderLinearMilestoneSaveCall,
16
+ renderLinearMilestoneSaveResult,
17
+ } from '../renderers/milestones';
8
18
 
9
19
  export function milestoneTools() {
10
20
  return [
@@ -16,20 +26,16 @@ export function milestoneTools() {
16
26
  ...PaginationParams,
17
27
  ...FilterParam,
18
28
  }),
29
+ renderCall: renderLinearMilestoneListCall,
19
30
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
20
31
  return withLinearAuth(ctx, signal, async (apiKey) => {
21
32
  const variables = compactObject({
22
- after: params.after,
23
- before: params.before,
33
+ ...paginationVariables(params, 20),
24
34
  filter: asObject(params.filter),
25
- first: params.first ?? 20,
26
- includeArchived: params.includeArchived,
27
- last: params.last,
28
- orderBy: params.orderBy,
29
35
  });
30
36
 
31
37
  const data = await linearGraphQL<{
32
- projectMilestones: { nodes: Array<JsonObject> };
38
+ projectMilestones: LinearConnection<JsonObject>;
33
39
  }>(
34
40
  apiKey,
35
41
  `query ListMilestones(
@@ -53,6 +59,12 @@ export function milestoneTools() {
53
59
  nodes {
54
60
  ${MILESTONE_SELECTION}
55
61
  }
62
+ pageInfo {
63
+ hasNextPage
64
+ hasPreviousPage
65
+ startCursor
66
+ endCursor
67
+ }
56
68
  }
57
69
  }`,
58
70
  variables,
@@ -60,12 +72,14 @@ export function milestoneTools() {
60
72
  );
61
73
 
62
74
  const milestones = data.projectMilestones.nodes;
75
+ const pageInfo = data.projectMilestones.pageInfo;
63
76
  return {
64
- content: [{ type: 'text', text: JSON.stringify({ milestones }, null, 2) }],
65
- details: { milestones },
77
+ content: [{ type: 'text', text: JSON.stringify({ milestones, pageInfo }, null, 2) }],
78
+ details: { milestones, pageInfo },
66
79
  };
67
80
  });
68
81
  },
82
+ renderResult: renderLinearMilestoneListResult,
69
83
  }),
70
84
  defineTool({
71
85
  name: 'linear_get_milestone',
@@ -74,6 +88,7 @@ export function milestoneTools() {
74
88
  parameters: Type.Object({
75
89
  milestoneId: Type.String(),
76
90
  }),
91
+ renderCall: renderLinearMilestoneGetCall,
77
92
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
78
93
  return withLinearAuth(ctx, signal, async (apiKey) => {
79
94
  const data = await linearGraphQL<{
@@ -98,6 +113,7 @@ export function milestoneTools() {
98
113
  };
99
114
  });
100
115
  },
116
+ renderResult: renderLinearMilestoneResult('Milestone'),
101
117
  }),
102
118
  defineTool({
103
119
  name: 'linear_save_milestone',
@@ -115,6 +131,7 @@ export function milestoneTools() {
115
131
  targetDate: Type.Optional(Type.String()),
116
132
  ...RawInputParam,
117
133
  }),
134
+ renderCall: renderLinearMilestoneSaveCall,
118
135
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
119
136
  return withLinearAuth(ctx, signal, async (apiKey) => {
120
137
  const rawInput = asObject(params.input) || {};
@@ -212,6 +229,7 @@ export function milestoneTools() {
212
229
  };
213
230
  });
214
231
  },
232
+ renderResult: renderLinearMilestoneSaveResult(),
215
233
  }),
216
234
  defineTool({
217
235
  name: 'linear_delete_milestone',
@@ -220,6 +238,7 @@ export function milestoneTools() {
220
238
  parameters: Type.Object({
221
239
  milestoneId: Type.String(),
222
240
  }),
241
+ renderCall: renderLinearMilestoneDeleteCall,
223
242
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
224
243
  return withLinearAuth(ctx, signal, async (apiKey) => {
225
244
  const data = await linearGraphQL<{
@@ -245,6 +264,7 @@ export function milestoneTools() {
245
264
  };
246
265
  });
247
266
  },
267
+ renderResult: renderLinearMilestoneDeleteResult,
248
268
  }),
249
269
  ];
250
270
  }
@@ -1,10 +1,19 @@
1
1
  import { defineTool } from '@mariozechner/pi-coding-agent';
2
2
  import { Type } from '@sinclair/typebox';
3
3
  import { withLinearAuth, linearGraphQL } from '../client';
4
- import { PaginationParams, FilterParam, RawInputParam } from '../params';
4
+ import { PaginationParams, paginationVariables, FilterParam, RawInputParam } from '../params';
5
5
  import { PROJECT_LABEL_SELECTION } from '../selections';
6
- import type { JsonObject } from '../types';
6
+ import type { JsonObject, LinearConnection } from '../types';
7
7
  import { compactObject, asObject, asString } from '../util';
8
+ import {
9
+ renderLinearCreateProjectLabelCall,
10
+ renderLinearDeleteProjectLabelCall,
11
+ renderLinearProjectLabelDeleteResult,
12
+ renderLinearProjectLabelListCall,
13
+ renderLinearProjectLabelListResult,
14
+ renderLinearProjectLabelResult,
15
+ renderLinearUpdateProjectLabelCall,
16
+ } from '../renderers/project-labels';
8
17
 
9
18
  export function projectLabelTools() {
10
19
  return [
@@ -16,20 +25,16 @@ export function projectLabelTools() {
16
25
  ...PaginationParams,
17
26
  ...FilterParam,
18
27
  }),
28
+ renderCall: renderLinearProjectLabelListCall,
19
29
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
20
30
  return withLinearAuth(ctx, signal, async (apiKey) => {
21
31
  const variables = compactObject({
22
- after: params.after,
23
- before: params.before,
32
+ ...paginationVariables(params, 50),
24
33
  filter: asObject(params.filter),
25
- first: params.first ?? 50,
26
- includeArchived: params.includeArchived,
27
- last: params.last,
28
- orderBy: params.orderBy,
29
34
  });
30
35
 
31
36
  const data = await linearGraphQL<{
32
- projectLabels: { nodes: Array<JsonObject> };
37
+ projectLabels: LinearConnection<JsonObject>;
33
38
  }>(
34
39
  apiKey,
35
40
  `query ListProjectLabels(
@@ -53,6 +58,12 @@ export function projectLabelTools() {
53
58
  nodes {
54
59
  ${PROJECT_LABEL_SELECTION}
55
60
  }
61
+ pageInfo {
62
+ hasNextPage
63
+ hasPreviousPage
64
+ startCursor
65
+ endCursor
66
+ }
56
67
  }
57
68
  }`,
58
69
  variables,
@@ -60,12 +71,14 @@ export function projectLabelTools() {
60
71
  );
61
72
 
62
73
  const labels = data.projectLabels.nodes;
74
+ const pageInfo = data.projectLabels.pageInfo;
63
75
  return {
64
- content: [{ type: 'text', text: JSON.stringify({ labels }, null, 2) }],
65
- details: { labels },
76
+ content: [{ type: 'text', text: JSON.stringify({ labels, pageInfo }, null, 2) }],
77
+ details: { labels, pageInfo },
66
78
  };
67
79
  });
68
80
  },
81
+ renderResult: renderLinearProjectLabelListResult,
69
82
  }),
70
83
  defineTool({
71
84
  name: 'linear_create_project_label',
@@ -79,6 +92,7 @@ export function projectLabelTools() {
79
92
  isGroup: Type.Optional(Type.Boolean()),
80
93
  ...RawInputParam,
81
94
  }),
95
+ renderCall: renderLinearCreateProjectLabelCall,
82
96
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
83
97
  return withLinearAuth(ctx, signal, async (apiKey) => {
84
98
  const rawInput = asObject(params.input) || {};
@@ -127,6 +141,7 @@ export function projectLabelTools() {
127
141
  };
128
142
  });
129
143
  },
144
+ renderResult: renderLinearProjectLabelResult('Created project label'),
130
145
  }),
131
146
  defineTool({
132
147
  name: 'linear_update_project_label',
@@ -141,6 +156,7 @@ export function projectLabelTools() {
141
156
  isGroup: Type.Optional(Type.Boolean()),
142
157
  ...RawInputParam,
143
158
  }),
159
+ renderCall: renderLinearUpdateProjectLabelCall,
144
160
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
145
161
  return withLinearAuth(ctx, signal, async (apiKey) => {
146
162
  const rawInput = asObject(params.input) || {};
@@ -189,6 +205,7 @@ export function projectLabelTools() {
189
205
  };
190
206
  });
191
207
  },
208
+ renderResult: renderLinearProjectLabelResult('Updated project label'),
192
209
  }),
193
210
  defineTool({
194
211
  name: 'linear_delete_project_label',
@@ -197,6 +214,7 @@ export function projectLabelTools() {
197
214
  parameters: Type.Object({
198
215
  id: Type.String(),
199
216
  }),
217
+ renderCall: renderLinearDeleteProjectLabelCall,
200
218
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
201
219
  return withLinearAuth(ctx, signal, async (apiKey) => {
202
220
  const data = await linearGraphQL<{
@@ -222,6 +240,7 @@ export function projectLabelTools() {
222
240
  };
223
241
  });
224
242
  },
243
+ renderResult: renderLinearProjectLabelDeleteResult,
225
244
  }),
226
245
  ];
227
246
  }
@@ -1,10 +1,19 @@
1
1
  import { defineTool } from '@mariozechner/pi-coding-agent';
2
2
  import { Type } from '@sinclair/typebox';
3
3
  import { withLinearAuth, linearGraphQL } from '../client';
4
- import { PaginationParams } from '../params';
4
+ import { PaginationParams, paginationVariables } from '../params';
5
5
  import { PROJECT_RELATION_SELECTION } from '../selections';
6
- import type { JsonObject } from '../types';
6
+ import type { JsonObject, LinearConnection } from '../types';
7
7
  import { compactObject } from '../util';
8
+ import {
9
+ renderLinearCreateProjectRelationCall,
10
+ renderLinearDeleteProjectRelationCall,
11
+ renderLinearDeleteProjectRelationResult,
12
+ renderLinearProjectRelationListCall,
13
+ renderLinearProjectRelationListResult,
14
+ renderLinearProjectRelationResult,
15
+ renderLinearUpdateProjectRelationCall,
16
+ } from '../renderers/project-relations';
8
17
 
9
18
  export function projectRelationTools() {
10
19
  return [
@@ -15,19 +24,13 @@ export function projectRelationTools() {
15
24
  parameters: Type.Object({
16
25
  ...PaginationParams,
17
26
  }),
27
+ renderCall: renderLinearProjectRelationListCall,
18
28
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
19
29
  return withLinearAuth(ctx, signal, async (apiKey) => {
20
- const variables = compactObject({
21
- after: params.after,
22
- before: params.before,
23
- first: params.first ?? 20,
24
- includeArchived: params.includeArchived,
25
- last: params.last,
26
- orderBy: params.orderBy,
27
- });
30
+ const variables = paginationVariables(params, 20);
28
31
 
29
32
  const data = await linearGraphQL<{
30
- projectRelations: { nodes: Array<JsonObject> };
33
+ projectRelations: LinearConnection<JsonObject>;
31
34
  }>(
32
35
  apiKey,
33
36
  `query ListProjectRelations(
@@ -49,6 +52,12 @@ export function projectRelationTools() {
49
52
  nodes {
50
53
  ${PROJECT_RELATION_SELECTION}
51
54
  }
55
+ pageInfo {
56
+ hasNextPage
57
+ hasPreviousPage
58
+ startCursor
59
+ endCursor
60
+ }
52
61
  }
53
62
  }`,
54
63
  variables,
@@ -56,12 +65,16 @@ export function projectRelationTools() {
56
65
  );
57
66
 
58
67
  const projectRelations = data.projectRelations.nodes;
68
+ const pageInfo = data.projectRelations.pageInfo;
59
69
  return {
60
- content: [{ type: 'text', text: JSON.stringify({ projectRelations }, null, 2) }],
61
- details: { projectRelations },
70
+ content: [
71
+ { type: 'text', text: JSON.stringify({ projectRelations, pageInfo }, null, 2) },
72
+ ],
73
+ details: { projectRelations, pageInfo },
62
74
  };
63
75
  });
64
76
  },
77
+ renderResult: renderLinearProjectRelationListResult,
65
78
  }),
66
79
  defineTool({
67
80
  name: 'linear_create_project_relation',
@@ -78,6 +91,7 @@ export function projectRelationTools() {
78
91
  projectMilestoneId: Type.Optional(Type.String()),
79
92
  relatedProjectMilestoneId: Type.Optional(Type.String()),
80
93
  }),
94
+ renderCall: renderLinearCreateProjectRelationCall,
81
95
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
82
96
  return withLinearAuth(ctx, signal, async (apiKey) => {
83
97
  const input = compactObject({
@@ -120,6 +134,7 @@ export function projectRelationTools() {
120
134
  };
121
135
  });
122
136
  },
137
+ renderResult: renderLinearProjectRelationResult('Created project relation'),
123
138
  }),
124
139
  defineTool({
125
140
  name: 'linear_update_project_relation',
@@ -135,6 +150,7 @@ export function projectRelationTools() {
135
150
  projectMilestoneId: Type.Optional(Type.String()),
136
151
  relatedProjectMilestoneId: Type.Optional(Type.String()),
137
152
  }),
153
+ renderCall: renderLinearUpdateProjectRelationCall,
138
154
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
139
155
  return withLinearAuth(ctx, signal, async (apiKey) => {
140
156
  const input = compactObject({
@@ -181,6 +197,7 @@ export function projectRelationTools() {
181
197
  };
182
198
  });
183
199
  },
200
+ renderResult: renderLinearProjectRelationResult('Updated project relation'),
184
201
  }),
185
202
  defineTool({
186
203
  name: 'linear_delete_project_relation',
@@ -189,6 +206,7 @@ export function projectRelationTools() {
189
206
  parameters: Type.Object({
190
207
  id: Type.String(),
191
208
  }),
209
+ renderCall: renderLinearDeleteProjectRelationCall,
192
210
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
193
211
  return withLinearAuth(ctx, signal, async (apiKey) => {
194
212
  const data = await linearGraphQL<{
@@ -214,6 +232,7 @@ export function projectRelationTools() {
214
232
  };
215
233
  });
216
234
  },
235
+ renderResult: renderLinearDeleteProjectRelationResult,
217
236
  }),
218
237
  ];
219
238
  }