@elizaos/plugin-linear 1.2.5

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/dist/index.js ADDED
@@ -0,0 +1,1333 @@
1
+ // src/services/linear.ts
2
+ import { logger, Service } from "@elizaos/core";
3
+ import { LinearClient } from "@linear/sdk";
4
+
5
+ // src/types/index.ts
6
+ var LinearAPIError = class extends Error {
7
+ constructor(message, status, response) {
8
+ super(message);
9
+ this.status = status;
10
+ this.response = response;
11
+ this.name = "LinearAPIError";
12
+ }
13
+ };
14
+ var LinearAuthenticationError = class extends LinearAPIError {
15
+ constructor(message) {
16
+ super(message, 401);
17
+ this.name = "LinearAuthenticationError";
18
+ }
19
+ };
20
+ var LinearRateLimitError = class extends LinearAPIError {
21
+ constructor(message, resetTime) {
22
+ super(message, 429);
23
+ this.resetTime = resetTime;
24
+ this.name = "LinearRateLimitError";
25
+ }
26
+ };
27
+
28
+ // src/services/linear.ts
29
+ var _LinearService = class _LinearService extends Service {
30
+ constructor(runtime) {
31
+ super(runtime);
32
+ this.capabilityDescription = "Linear API integration for issue tracking, project management, and team collaboration";
33
+ this.activityLog = [];
34
+ const apiKey = runtime?.getSetting("LINEAR_API_KEY");
35
+ const workspaceId = runtime?.getSetting("LINEAR_WORKSPACE_ID");
36
+ if (!apiKey) {
37
+ throw new LinearAuthenticationError("Linear API key is required");
38
+ }
39
+ this.linearConfig = {
40
+ LINEAR_API_KEY: apiKey,
41
+ LINEAR_WORKSPACE_ID: workspaceId
42
+ };
43
+ this.workspaceId = workspaceId;
44
+ this.config = {
45
+ LINEAR_API_KEY: apiKey,
46
+ LINEAR_WORKSPACE_ID: workspaceId
47
+ };
48
+ this.client = new LinearClient({
49
+ apiKey: this.linearConfig.LINEAR_API_KEY
50
+ });
51
+ }
52
+ static async start(runtime) {
53
+ const service = new _LinearService(runtime);
54
+ await service.validateConnection();
55
+ logger.info("Linear service started successfully");
56
+ return service;
57
+ }
58
+ async stop() {
59
+ this.activityLog = [];
60
+ logger.info("Linear service stopped");
61
+ }
62
+ // Validate the API connection
63
+ async validateConnection() {
64
+ try {
65
+ const viewer = await this.client.viewer;
66
+ logger.info(`Linear connected as user: ${viewer.email}`);
67
+ } catch (error) {
68
+ throw new LinearAuthenticationError("Failed to authenticate with Linear API");
69
+ }
70
+ }
71
+ // Log activity
72
+ logActivity(action, resourceType, resourceId, details, success, error) {
73
+ const activity = {
74
+ id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
75
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
76
+ action,
77
+ resource_type: resourceType,
78
+ resource_id: resourceId,
79
+ details,
80
+ success,
81
+ error
82
+ };
83
+ this.activityLog.push(activity);
84
+ if (this.activityLog.length > 1e3) {
85
+ this.activityLog = this.activityLog.slice(-1e3);
86
+ }
87
+ }
88
+ // Get activity log
89
+ getActivityLog(limit, filter) {
90
+ let filtered = [...this.activityLog];
91
+ if (filter) {
92
+ filtered = filtered.filter((item) => {
93
+ return Object.entries(filter).every(([key, value]) => {
94
+ return item[key] === value;
95
+ });
96
+ });
97
+ }
98
+ return filtered.slice(-(limit || 100));
99
+ }
100
+ // Clear activity log
101
+ clearActivityLog() {
102
+ this.activityLog = [];
103
+ logger.info("Linear activity log cleared");
104
+ }
105
+ // Team operations
106
+ async getTeams() {
107
+ try {
108
+ const teams = await this.client.teams();
109
+ const teamList = await teams.nodes;
110
+ this.logActivity("list_teams", "team", "all", { count: teamList.length }, true);
111
+ return teamList;
112
+ } catch (error) {
113
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
114
+ this.logActivity("list_teams", "team", "all", {}, false, errorMessage);
115
+ throw new LinearAPIError(`Failed to fetch teams: ${errorMessage}`);
116
+ }
117
+ }
118
+ async getTeam(teamId) {
119
+ try {
120
+ const team = await this.client.team(teamId);
121
+ this.logActivity("get_team", "team", teamId, { name: team.name }, true);
122
+ return team;
123
+ } catch (error) {
124
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
125
+ this.logActivity("get_team", "team", teamId, {}, false, errorMessage);
126
+ throw new LinearAPIError(`Failed to fetch team: ${errorMessage}`);
127
+ }
128
+ }
129
+ // Issue operations
130
+ async createIssue(input) {
131
+ try {
132
+ const issuePayload = await this.client.createIssue({
133
+ title: input.title,
134
+ description: input.description,
135
+ teamId: input.teamId,
136
+ priority: input.priority,
137
+ assigneeId: input.assigneeId,
138
+ labelIds: input.labelIds,
139
+ projectId: input.projectId,
140
+ stateId: input.stateId,
141
+ estimate: input.estimate,
142
+ dueDate: input.dueDate
143
+ });
144
+ const issue = await issuePayload.issue;
145
+ if (!issue) {
146
+ throw new Error("Failed to create issue");
147
+ }
148
+ this.logActivity("create_issue", "issue", issue.id, {
149
+ title: input.title,
150
+ teamId: input.teamId
151
+ }, true);
152
+ return issue;
153
+ } catch (error) {
154
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
155
+ this.logActivity("create_issue", "issue", "new", input, false, errorMessage);
156
+ throw new LinearAPIError(`Failed to create issue: ${errorMessage}`);
157
+ }
158
+ }
159
+ async getIssue(issueId) {
160
+ try {
161
+ const issue = await this.client.issue(issueId);
162
+ this.logActivity("get_issue", "issue", issueId, {
163
+ title: issue.title,
164
+ identifier: issue.identifier
165
+ }, true);
166
+ return issue;
167
+ } catch (error) {
168
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
169
+ this.logActivity("get_issue", "issue", issueId, {}, false, errorMessage);
170
+ throw new LinearAPIError(`Failed to fetch issue: ${errorMessage}`);
171
+ }
172
+ }
173
+ async updateIssue(issueId, updates) {
174
+ try {
175
+ const updatePayload = await this.client.updateIssue(issueId, {
176
+ title: updates.title,
177
+ description: updates.description,
178
+ priority: updates.priority,
179
+ assigneeId: updates.assigneeId,
180
+ labelIds: updates.labelIds,
181
+ projectId: updates.projectId,
182
+ stateId: updates.stateId,
183
+ estimate: updates.estimate,
184
+ dueDate: updates.dueDate
185
+ });
186
+ const issue = await updatePayload.issue;
187
+ if (!issue) {
188
+ throw new Error("Failed to update issue");
189
+ }
190
+ this.logActivity("update_issue", "issue", issueId, updates, true);
191
+ return issue;
192
+ } catch (error) {
193
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
194
+ this.logActivity("update_issue", "issue", issueId, updates, false, errorMessage);
195
+ throw new LinearAPIError(`Failed to update issue: ${errorMessage}`);
196
+ }
197
+ }
198
+ async searchIssues(filters) {
199
+ try {
200
+ const query = this.client.issues({
201
+ first: filters.limit || 50,
202
+ filter: filters.query ? {
203
+ or: [
204
+ { title: { containsIgnoreCase: filters.query } },
205
+ { description: { containsIgnoreCase: filters.query } }
206
+ ]
207
+ } : void 0
208
+ });
209
+ const issues = await query;
210
+ const issueList = await issues.nodes;
211
+ this.logActivity("search_issues", "issue", "search", {
212
+ filters,
213
+ count: issueList.length
214
+ }, true);
215
+ return issueList;
216
+ } catch (error) {
217
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
218
+ this.logActivity("search_issues", "issue", "search", filters, false, errorMessage);
219
+ throw new LinearAPIError(`Failed to search issues: ${errorMessage}`);
220
+ }
221
+ }
222
+ // Comment operations
223
+ async createComment(input) {
224
+ try {
225
+ const commentPayload = await this.client.createComment({
226
+ body: input.body,
227
+ issueId: input.issueId
228
+ });
229
+ const comment = await commentPayload.comment;
230
+ if (!comment) {
231
+ throw new Error("Failed to create comment");
232
+ }
233
+ this.logActivity("create_comment", "comment", comment.id, {
234
+ issueId: input.issueId,
235
+ bodyLength: input.body.length
236
+ }, true);
237
+ return comment;
238
+ } catch (error) {
239
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
240
+ this.logActivity("create_comment", "comment", "new", input, false, errorMessage);
241
+ throw new LinearAPIError(`Failed to create comment: ${errorMessage}`);
242
+ }
243
+ }
244
+ // Project operations
245
+ async getProjects(teamId) {
246
+ try {
247
+ const query = this.client.projects({
248
+ first: 100
249
+ });
250
+ const projects = await query;
251
+ let projectList = await projects.nodes;
252
+ if (teamId) {
253
+ const filteredProjects = await Promise.all(
254
+ projectList.map(async (project) => {
255
+ const projectTeams = await project.teams();
256
+ const teamsList = await projectTeams.nodes;
257
+ const hasTeam = teamsList.some((team) => team.id === teamId);
258
+ return hasTeam ? project : null;
259
+ })
260
+ );
261
+ projectList = filteredProjects.filter(Boolean);
262
+ }
263
+ this.logActivity("list_projects", "project", "all", {
264
+ count: projectList.length,
265
+ teamId
266
+ }, true);
267
+ return projectList;
268
+ } catch (error) {
269
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
270
+ this.logActivity("list_projects", "project", "all", { teamId }, false, errorMessage);
271
+ throw new LinearAPIError(`Failed to fetch projects: ${errorMessage}`);
272
+ }
273
+ }
274
+ async getProject(projectId) {
275
+ try {
276
+ const project = await this.client.project(projectId);
277
+ this.logActivity("get_project", "project", projectId, {
278
+ name: project.name
279
+ }, true);
280
+ return project;
281
+ } catch (error) {
282
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
283
+ this.logActivity("get_project", "project", projectId, {}, false, errorMessage);
284
+ throw new LinearAPIError(`Failed to fetch project: ${errorMessage}`);
285
+ }
286
+ }
287
+ // User operations
288
+ async getUsers() {
289
+ try {
290
+ const users = await this.client.users();
291
+ const userList = await users.nodes;
292
+ this.logActivity("list_users", "user", "all", {
293
+ count: userList.length
294
+ }, true);
295
+ return userList;
296
+ } catch (error) {
297
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
298
+ this.logActivity("list_users", "user", "all", {}, false, errorMessage);
299
+ throw new LinearAPIError(`Failed to fetch users: ${errorMessage}`);
300
+ }
301
+ }
302
+ async getCurrentUser() {
303
+ try {
304
+ const user = await this.client.viewer;
305
+ this.logActivity("get_current_user", "user", user.id, {
306
+ email: user.email,
307
+ name: user.name
308
+ }, true);
309
+ return user;
310
+ } catch (error) {
311
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
312
+ this.logActivity("get_current_user", "user", "current", {}, false, errorMessage);
313
+ throw new LinearAPIError(`Failed to fetch current user: ${errorMessage}`);
314
+ }
315
+ }
316
+ // Label operations
317
+ async getLabels(teamId) {
318
+ try {
319
+ const query = this.client.issueLabels({
320
+ first: 100,
321
+ filter: teamId ? {
322
+ team: { id: { eq: teamId } }
323
+ } : void 0
324
+ });
325
+ const labels = await query;
326
+ const labelList = await labels.nodes;
327
+ this.logActivity("list_labels", "label", "all", {
328
+ count: labelList.length,
329
+ teamId
330
+ }, true);
331
+ return labelList;
332
+ } catch (error) {
333
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
334
+ this.logActivity("list_labels", "label", "all", { teamId }, false, errorMessage);
335
+ throw new LinearAPIError(`Failed to fetch labels: ${errorMessage}`);
336
+ }
337
+ }
338
+ // Workflow state operations
339
+ async getWorkflowStates(teamId) {
340
+ try {
341
+ const states = await this.client.workflowStates({
342
+ filter: {
343
+ team: { id: { eq: teamId } }
344
+ }
345
+ });
346
+ const stateList = await states.nodes;
347
+ this.logActivity("list_workflow_states", "team", teamId, {
348
+ count: stateList.length
349
+ }, true);
350
+ return stateList;
351
+ } catch (error) {
352
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
353
+ this.logActivity("list_workflow_states", "team", teamId, {}, false, errorMessage);
354
+ throw new LinearAPIError(`Failed to fetch workflow states: ${errorMessage}`);
355
+ }
356
+ }
357
+ };
358
+ _LinearService.serviceType = "linear";
359
+ var LinearService = _LinearService;
360
+
361
+ // src/actions/createIssue.ts
362
+ import {
363
+ logger as logger2
364
+ } from "@elizaos/core";
365
+ var createIssueTemplate = `Create a new Linear issue based on the user's request. Extract the necessary information from the conversation.
366
+
367
+ Recent conversation:
368
+ {{recentMessages}}
369
+
370
+ When creating the issue:
371
+ 1. The title should be clear and concise
372
+ 2. The description should include all relevant details from the conversation
373
+ 3. Determine the appropriate team based on context
374
+ 4. Set priority if mentioned (1=Urgent, 2=High, 3=Normal, 4=Low)
375
+ 5. If no team is specified, use the default team
376
+
377
+ Response format should be a valid JSON block:
378
+ \`\`\`json
379
+ {
380
+ "title": "Clear, actionable issue title",
381
+ "description": "Detailed description with context from the conversation",
382
+ "teamId": "team-id or null to use default",
383
+ "priority": 3,
384
+ "shouldCreate": true
385
+ }
386
+ \`\`\`
387
+ `;
388
+ var createLinearIssueAction = {
389
+ name: "CREATE_LINEAR_ISSUE",
390
+ description: "Create a new issue in Linear",
391
+ similes: ["create issue", "new issue", "file issue", "report issue", "create ticket", "new ticket"],
392
+ async validate(runtime, _message, state) {
393
+ try {
394
+ const linearService = runtime.getService("linear");
395
+ return !!linearService;
396
+ } catch {
397
+ return false;
398
+ }
399
+ },
400
+ async handler(runtime, message, state, options) {
401
+ try {
402
+ const linearService = runtime.getService("linear");
403
+ if (!linearService) {
404
+ throw new Error("Linear service not available");
405
+ }
406
+ if (options?.title && options?.teamId) {
407
+ const issueInput2 = {
408
+ title: String(options.title),
409
+ description: options.description ? String(options.description) : void 0,
410
+ teamId: String(options.teamId),
411
+ priority: options.priority ? Number(options.priority) : 3,
412
+ assigneeId: options.assigneeId ? String(options.assigneeId) : void 0,
413
+ labelIds: options.labelIds ? options.labelIds : void 0,
414
+ projectId: options.projectId ? String(options.projectId) : void 0
415
+ };
416
+ const issue2 = await linearService.createIssue(issueInput2);
417
+ return {
418
+ success: true,
419
+ data: {
420
+ issue: {
421
+ id: issue2.id,
422
+ identifier: issue2.identifier,
423
+ title: issue2.title,
424
+ url: issue2.url
425
+ }
426
+ },
427
+ metadata: {
428
+ issueId: issue2.id,
429
+ identifier: issue2.identifier
430
+ }
431
+ };
432
+ }
433
+ const response = await runtime.generateText({
434
+ messages: state.messages || [],
435
+ context: createIssueTemplate
436
+ });
437
+ const parsed = JSON.parse(response.trim().replace(/```json\n?|\n?```/g, ""));
438
+ if (!parsed.shouldCreate) {
439
+ return {
440
+ success: false,
441
+ error: "Not enough information to create an issue"
442
+ };
443
+ }
444
+ let teamId = parsed.teamId;
445
+ let teamName;
446
+ if (!teamId) {
447
+ const teams = await linearService.getTeams();
448
+ if (teams.length === 0) {
449
+ throw new Error("No teams available in Linear workspace");
450
+ }
451
+ teamId = teams[0].id;
452
+ teamName = teams[0].name;
453
+ } else {
454
+ try {
455
+ const team = await linearService.getTeam(teamId);
456
+ teamName = team.name;
457
+ } catch {
458
+ }
459
+ }
460
+ const issueInput = {
461
+ title: parsed.title,
462
+ description: parsed.description,
463
+ teamId,
464
+ priority: parsed.priority || 3
465
+ };
466
+ const issue = await linearService.createIssue(issueInput);
467
+ logger2.info(`Created Linear issue: ${issue.identifier} - ${issue.title}`);
468
+ return {
469
+ success: true,
470
+ data: {
471
+ issue: {
472
+ id: issue.id,
473
+ identifier: issue.identifier,
474
+ title: issue.title,
475
+ url: issue.url,
476
+ teamName
477
+ }
478
+ },
479
+ metadata: {
480
+ issueId: issue.id,
481
+ identifier: issue.identifier
482
+ }
483
+ };
484
+ } catch (error) {
485
+ logger2.error("Failed to create Linear issue:", error);
486
+ return {
487
+ success: false,
488
+ error: error instanceof Error ? error.message : "Failed to create issue"
489
+ };
490
+ }
491
+ },
492
+ examples: [
493
+ {
494
+ input: "Create a new issue: Fix login button not working on mobile devices",
495
+ output: "Created issue ENG-123: Fix login button not working on mobile devices",
496
+ explanation: "Creates a new issue with the provided title"
497
+ },
498
+ {
499
+ input: "File a bug report: Users cannot upload images larger than 5MB, getting timeout errors",
500
+ output: "Created issue BUG-456: Image upload timeout for files > 5MB",
501
+ explanation: "Creates a bug report with extracted details"
502
+ },
503
+ {
504
+ input: "Create a high priority ticket for the payment processing error we discussed",
505
+ output: "Created high priority issue PAY-789: Payment processing error investigation",
506
+ explanation: "Creates an issue with priority based on conversation context"
507
+ }
508
+ ]
509
+ };
510
+
511
+ // src/actions/getIssue.ts
512
+ import {
513
+ logger as logger3
514
+ } from "@elizaos/core";
515
+ var getLinearIssueAction = {
516
+ name: "GET_LINEAR_ISSUE",
517
+ description: "Get details of a specific Linear issue by ID or identifier",
518
+ similes: ["get issue", "show issue", "fetch issue", "view issue", "issue details", "what is issue"],
519
+ async validate(runtime, _message, state) {
520
+ try {
521
+ const linearService = runtime.getService("linear");
522
+ return !!linearService;
523
+ } catch {
524
+ return false;
525
+ }
526
+ },
527
+ async handler(runtime, message, state, options) {
528
+ try {
529
+ const linearService = runtime.getService("linear");
530
+ if (!linearService) {
531
+ throw new Error("Linear service not available");
532
+ }
533
+ let issueId;
534
+ if (options?.issueId) {
535
+ issueId = String(options.issueId);
536
+ } else {
537
+ const issuePattern = /\b[A-Z]+-\d+\b/;
538
+ const match = message.content.text?.match(issuePattern);
539
+ if (match) {
540
+ issueId = match[0];
541
+ }
542
+ }
543
+ if (!issueId) {
544
+ return {
545
+ success: false,
546
+ error: "No issue ID or identifier provided"
547
+ };
548
+ }
549
+ const issue = await linearService.getIssue(issueId);
550
+ const [assignee, state2, team, labels] = await Promise.all([
551
+ issue.assignee,
552
+ issue.state,
553
+ issue.team,
554
+ issue.labels()
555
+ ]);
556
+ const labelList = await labels.nodes;
557
+ const issueData = {
558
+ id: issue.id,
559
+ identifier: issue.identifier,
560
+ title: issue.title,
561
+ description: issue.description,
562
+ url: issue.url,
563
+ priority: issue.priority,
564
+ priorityLabel: issue.priorityLabel,
565
+ estimate: issue.estimate,
566
+ createdAt: issue.createdAt,
567
+ updatedAt: issue.updatedAt,
568
+ dueDate: issue.dueDate,
569
+ assignee: assignee ? {
570
+ id: assignee.id,
571
+ name: assignee.name,
572
+ email: assignee.email
573
+ } : null,
574
+ state: {
575
+ id: state2.id,
576
+ name: state2.name,
577
+ type: state2.type,
578
+ color: state2.color
579
+ },
580
+ team: {
581
+ id: team.id,
582
+ name: team.name,
583
+ key: team.key
584
+ },
585
+ labels: labelList.map((label) => ({
586
+ id: label.id,
587
+ name: label.name,
588
+ color: label.color
589
+ }))
590
+ };
591
+ logger3.info(`Retrieved Linear issue: ${issue.identifier}`);
592
+ return {
593
+ success: true,
594
+ data: {
595
+ issue: issueData
596
+ },
597
+ metadata: {
598
+ issueId: issue.id,
599
+ identifier: issue.identifier
600
+ }
601
+ };
602
+ } catch (error) {
603
+ logger3.error("Failed to get Linear issue:", error);
604
+ return {
605
+ success: false,
606
+ error: error instanceof Error ? error.message : "Failed to get issue"
607
+ };
608
+ }
609
+ },
610
+ examples: [
611
+ {
612
+ input: "Show me issue ENG-123",
613
+ output: "Issue ENG-123: Fix login button on mobile\nStatus: In Progress\nAssignee: John Doe\nPriority: High",
614
+ explanation: "Retrieves issue details by identifier"
615
+ },
616
+ {
617
+ input: "Get details for BUG-456",
618
+ output: "Issue BUG-456: Image upload timeout\nStatus: Todo\nAssignee: Unassigned\nPriority: Urgent\nLabels: bug, performance",
619
+ explanation: "Fetches comprehensive issue information"
620
+ },
621
+ {
622
+ input: "What is the status of FEAT-789?",
623
+ output: "Issue FEAT-789: Add dark mode support\nStatus: Done\nAssignee: Jane Smith\nCompleted: 2 days ago",
624
+ explanation: "Shows current status and details of an issue"
625
+ }
626
+ ]
627
+ };
628
+
629
+ // src/actions/updateIssue.ts
630
+ import {
631
+ logger as logger4
632
+ } from "@elizaos/core";
633
+ var updateLinearIssueAction = {
634
+ name: "UPDATE_LINEAR_ISSUE",
635
+ description: "Update an existing Linear issue",
636
+ similes: ["update issue", "modify issue", "change issue", "edit issue"],
637
+ async validate(runtime, _message, state) {
638
+ try {
639
+ const linearService = runtime.getService("linear");
640
+ return !!linearService;
641
+ } catch {
642
+ return false;
643
+ }
644
+ },
645
+ async handler(runtime, message, state, options) {
646
+ try {
647
+ const linearService = runtime.getService("linear");
648
+ if (!linearService) {
649
+ throw new Error("Linear service not available");
650
+ }
651
+ const issueId = options?.issueId ? String(options.issueId) : void 0;
652
+ if (!issueId) {
653
+ return {
654
+ success: false,
655
+ error: "Issue ID is required"
656
+ };
657
+ }
658
+ const updates = {};
659
+ if (options?.title !== void 0) updates.title = String(options.title);
660
+ if (options?.description !== void 0) updates.description = String(options.description);
661
+ if (options?.priority !== void 0) updates.priority = Number(options.priority);
662
+ if (options?.assigneeId !== void 0) updates.assigneeId = String(options.assigneeId);
663
+ if (options?.stateId !== void 0) updates.stateId = String(options.stateId);
664
+ if (options?.labelIds !== void 0) updates.labelIds = options.labelIds;
665
+ if (options?.projectId !== void 0) updates.projectId = String(options.projectId);
666
+ if (options?.estimate !== void 0) updates.estimate = Number(options.estimate);
667
+ if (options?.dueDate !== void 0) updates.dueDate = new Date(String(options.dueDate));
668
+ const issue = await linearService.updateIssue(issueId, updates);
669
+ logger4.info(`Updated Linear issue: ${issue.identifier}`);
670
+ return {
671
+ success: true,
672
+ data: {
673
+ issue: {
674
+ id: issue.id,
675
+ identifier: issue.identifier,
676
+ title: issue.title,
677
+ url: issue.url
678
+ }
679
+ },
680
+ metadata: {
681
+ issueId: issue.id,
682
+ identifier: issue.identifier,
683
+ updates: Object.keys(updates)
684
+ }
685
+ };
686
+ } catch (error) {
687
+ logger4.error("Failed to update Linear issue:", error);
688
+ return {
689
+ success: false,
690
+ error: error instanceof Error ? error.message : "Failed to update issue"
691
+ };
692
+ }
693
+ },
694
+ examples: [
695
+ {
696
+ input: 'Update issue ENG-123 title to "Fix login button on all devices"',
697
+ output: "Updated issue ENG-123: Fix login button on all devices",
698
+ explanation: "Updates the title of an existing issue"
699
+ }
700
+ ]
701
+ };
702
+
703
+ // src/actions/searchIssues.ts
704
+ import {
705
+ logger as logger5
706
+ } from "@elizaos/core";
707
+ var searchIssuesTemplate = `Extract search criteria from the user's request to search Linear issues.
708
+
709
+ Recent conversation:
710
+ {{recentMessages}}
711
+
712
+ Extract search filters like:
713
+ - query: Text to search in title/description
714
+ - state: Issue states (todo, in-progress, done, canceled)
715
+ - assignee: Assignee names or IDs
716
+ - label: Label names
717
+ - priority: Priority levels (1=Urgent, 2=High, 3=Normal, 4=Low)
718
+ - team: Team name or ID
719
+ - project: Project name or ID
720
+
721
+ Response format should be a valid JSON block:
722
+ \`\`\`json
723
+ {
724
+ "query": "search text or null",
725
+ "state": ["state1", "state2"] or null,
726
+ "assignee": ["assignee"] or null,
727
+ "label": ["label1", "label2"] or null,
728
+ "priority": [1, 2] or null,
729
+ "team": "team-name" or null,
730
+ "project": "project-name" or null,
731
+ "limit": 20
732
+ }
733
+ \`\`\`
734
+ `;
735
+ var searchLinearIssuesAction = {
736
+ name: "SEARCH_LINEAR_ISSUES",
737
+ description: "Search for issues in Linear based on various criteria",
738
+ similes: ["search issues", "find issues", "list issues", "show issues", "query issues", "filter issues"],
739
+ async validate(runtime, _message, state) {
740
+ try {
741
+ const linearService = runtime.getService("linear");
742
+ return !!linearService;
743
+ } catch {
744
+ return false;
745
+ }
746
+ },
747
+ async handler(runtime, message, state, options) {
748
+ try {
749
+ const linearService = runtime.getService("linear");
750
+ if (!linearService) {
751
+ throw new Error("Linear service not available");
752
+ }
753
+ let filters;
754
+ if (options && Object.keys(options).length > 0) {
755
+ filters = {
756
+ query: options.query ? String(options.query) : void 0,
757
+ state: options.state,
758
+ assignee: options.assignee,
759
+ label: options.label,
760
+ priority: options.priority,
761
+ team: options.team ? String(options.team) : void 0,
762
+ project: options.project ? String(options.project) : void 0,
763
+ limit: options.limit ? Number(options.limit) : 20
764
+ };
765
+ } else {
766
+ const response = await runtime.generateText({
767
+ messages: state.messages || [],
768
+ context: searchIssuesTemplate
769
+ });
770
+ filters = JSON.parse(response.trim().replace(/```json\n?|\n?```/g, ""));
771
+ }
772
+ if (!filters.limit) {
773
+ filters.limit = 20;
774
+ }
775
+ const issues = await linearService.searchIssues(filters);
776
+ const issuesWithDetails = await Promise.all(
777
+ issues.map(async (issue) => {
778
+ const [assignee, state2, team] = await Promise.all([
779
+ issue.assignee,
780
+ issue.state,
781
+ issue.team
782
+ ]);
783
+ return {
784
+ id: issue.id,
785
+ identifier: issue.identifier,
786
+ title: issue.title,
787
+ url: issue.url,
788
+ priority: issue.priority,
789
+ priorityLabel: issue.priorityLabel,
790
+ createdAt: issue.createdAt,
791
+ updatedAt: issue.updatedAt,
792
+ assignee: assignee ? assignee.name : "Unassigned",
793
+ state: state2.name,
794
+ team: team.name
795
+ };
796
+ })
797
+ );
798
+ logger5.info(`Found ${issues.length} Linear issues matching criteria`);
799
+ return {
800
+ success: true,
801
+ data: {
802
+ issues: issuesWithDetails,
803
+ count: issues.length,
804
+ filters
805
+ },
806
+ metadata: {
807
+ searchFilters: filters,
808
+ resultCount: issues.length
809
+ }
810
+ };
811
+ } catch (error) {
812
+ logger5.error("Failed to search Linear issues:", error);
813
+ return {
814
+ success: false,
815
+ error: error instanceof Error ? error.message : "Failed to search issues"
816
+ };
817
+ }
818
+ },
819
+ examples: [
820
+ {
821
+ input: "Show me all open bugs",
822
+ output: 'Found 5 open issues labeled as "bug":\n1. BUG-123: Login timeout issue\n2. BUG-124: Image upload fails\n...',
823
+ explanation: "Searches for issues with bug label in open states"
824
+ },
825
+ {
826
+ input: "Find high priority issues assigned to me",
827
+ output: "Found 3 high priority issues assigned to you:\n1. FEAT-456: Implement user dashboard\n2. BUG-789: Fix payment processing\n...",
828
+ explanation: "Searches for high priority issues assigned to the current user"
829
+ },
830
+ {
831
+ input: "Search for issues related to authentication",
832
+ output: 'Found 4 issues matching "authentication":\n1. SEC-001: Add 2FA support\n2. BUG-234: Password reset not working\n...',
833
+ explanation: "Performs text search across issue titles and descriptions"
834
+ }
835
+ ]
836
+ };
837
+
838
+ // src/actions/createComment.ts
839
+ import {
840
+ logger as logger6
841
+ } from "@elizaos/core";
842
+ var createLinearCommentAction = {
843
+ name: "CREATE_LINEAR_COMMENT",
844
+ description: "Add a comment to a Linear issue",
845
+ similes: ["comment on issue", "add comment", "reply to issue"],
846
+ async validate(runtime, _message, state) {
847
+ try {
848
+ const linearService = runtime.getService("linear");
849
+ return !!linearService;
850
+ } catch {
851
+ return false;
852
+ }
853
+ },
854
+ async handler(runtime, message, state, options) {
855
+ try {
856
+ const linearService = runtime.getService("linear");
857
+ if (!linearService) {
858
+ throw new Error("Linear service not available");
859
+ }
860
+ const issueId = options?.issueId ? String(options.issueId) : void 0;
861
+ const body = options?.body ? String(options.body) : message.content.text;
862
+ if (!issueId || !body) {
863
+ return {
864
+ success: false,
865
+ error: "Issue ID and comment body are required"
866
+ };
867
+ }
868
+ const commentInput = {
869
+ issueId,
870
+ body
871
+ };
872
+ const comment = await linearService.createComment(commentInput);
873
+ logger6.info(`Created comment on Linear issue: ${issueId}`);
874
+ return {
875
+ success: true,
876
+ data: {
877
+ comment: {
878
+ id: comment.id,
879
+ body: comment.body,
880
+ createdAt: comment.createdAt
881
+ }
882
+ },
883
+ metadata: {
884
+ commentId: comment.id,
885
+ issueId
886
+ }
887
+ };
888
+ } catch (error) {
889
+ logger6.error("Failed to create Linear comment:", error);
890
+ return {
891
+ success: false,
892
+ error: error instanceof Error ? error.message : "Failed to create comment"
893
+ };
894
+ }
895
+ },
896
+ examples: [
897
+ {
898
+ input: "Comment on ENG-123: This has been fixed in the latest release",
899
+ output: "Added comment to issue ENG-123",
900
+ explanation: "Adds a comment to an existing issue"
901
+ }
902
+ ]
903
+ };
904
+
905
+ // src/actions/listTeams.ts
906
+ import {
907
+ logger as logger7
908
+ } from "@elizaos/core";
909
+ var listLinearTeamsAction = {
910
+ name: "LIST_LINEAR_TEAMS",
911
+ description: "List all teams in the Linear workspace",
912
+ similes: ["show teams", "get teams", "list teams", "view teams"],
913
+ async validate(runtime, _message, state) {
914
+ try {
915
+ const linearService = runtime.getService("linear");
916
+ return !!linearService;
917
+ } catch {
918
+ return false;
919
+ }
920
+ },
921
+ async handler(runtime, message, state, options) {
922
+ try {
923
+ const linearService = runtime.getService("linear");
924
+ if (!linearService) {
925
+ throw new Error("Linear service not available");
926
+ }
927
+ const teams = await linearService.getTeams();
928
+ const teamsData = teams.map((team) => ({
929
+ id: team.id,
930
+ name: team.name,
931
+ key: team.key,
932
+ description: team.description
933
+ }));
934
+ logger7.info(`Retrieved ${teams.length} Linear teams`);
935
+ return {
936
+ success: true,
937
+ data: {
938
+ teams: teamsData,
939
+ count: teams.length
940
+ },
941
+ metadata: {
942
+ teamCount: teams.length
943
+ }
944
+ };
945
+ } catch (error) {
946
+ logger7.error("Failed to list Linear teams:", error);
947
+ return {
948
+ success: false,
949
+ error: error instanceof Error ? error.message : "Failed to list teams"
950
+ };
951
+ }
952
+ },
953
+ examples: [
954
+ {
955
+ input: "Show me all teams",
956
+ output: "Found 3 teams:\n1. Engineering (ENG)\n2. Design (DES)\n3. Product (PROD)",
957
+ explanation: "Lists all teams in the workspace"
958
+ }
959
+ ]
960
+ };
961
+
962
+ // src/actions/listProjects.ts
963
+ import {
964
+ logger as logger8
965
+ } from "@elizaos/core";
966
+ var listLinearProjectsAction = {
967
+ name: "LIST_LINEAR_PROJECTS",
968
+ description: "List projects in Linear, optionally filtered by team",
969
+ similes: ["show projects", "get projects", "list projects", "view projects"],
970
+ async validate(runtime, _message, state) {
971
+ try {
972
+ const linearService = runtime.getService("linear");
973
+ return !!linearService;
974
+ } catch {
975
+ return false;
976
+ }
977
+ },
978
+ async handler(runtime, message, state, options) {
979
+ try {
980
+ const linearService = runtime.getService("linear");
981
+ if (!linearService) {
982
+ throw new Error("Linear service not available");
983
+ }
984
+ const teamId = options?.teamId ? String(options.teamId) : void 0;
985
+ const projects = await linearService.getProjects(teamId);
986
+ const projectsData = await Promise.all(
987
+ projects.map(async (project) => {
988
+ const team = await project.team;
989
+ return {
990
+ id: project.id,
991
+ name: project.name,
992
+ description: project.description,
993
+ state: project.state,
994
+ teamName: team?.name,
995
+ startDate: project.startDate,
996
+ targetDate: project.targetDate
997
+ };
998
+ })
999
+ );
1000
+ logger8.info(`Retrieved ${projects.length} Linear projects`);
1001
+ return {
1002
+ success: true,
1003
+ data: {
1004
+ projects: projectsData,
1005
+ count: projects.length
1006
+ },
1007
+ metadata: {
1008
+ projectCount: projects.length,
1009
+ teamFilter: teamId
1010
+ }
1011
+ };
1012
+ } catch (error) {
1013
+ logger8.error("Failed to list Linear projects:", error);
1014
+ return {
1015
+ success: false,
1016
+ error: error instanceof Error ? error.message : "Failed to list projects"
1017
+ };
1018
+ }
1019
+ },
1020
+ examples: [
1021
+ {
1022
+ input: "Show me all projects",
1023
+ output: "Found 5 projects:\n1. Q1 2024 Roadmap\n2. Mobile App Redesign\n3. API v2 Migration...",
1024
+ explanation: "Lists all projects in the workspace"
1025
+ }
1026
+ ]
1027
+ };
1028
+
1029
+ // src/actions/getActivity.ts
1030
+ import {
1031
+ logger as logger9
1032
+ } from "@elizaos/core";
1033
+ var getLinearActivityAction = {
1034
+ name: "GET_LINEAR_ACTIVITY",
1035
+ description: "Get recent Linear activity log",
1036
+ similes: ["show activity", "get activity", "view activity", "activity log"],
1037
+ async validate(runtime, _message, state) {
1038
+ try {
1039
+ const linearService = runtime.getService("linear");
1040
+ return !!linearService;
1041
+ } catch {
1042
+ return false;
1043
+ }
1044
+ },
1045
+ async handler(runtime, message, state, options) {
1046
+ try {
1047
+ const linearService = runtime.getService("linear");
1048
+ if (!linearService) {
1049
+ throw new Error("Linear service not available");
1050
+ }
1051
+ const limit = options?.limit ? Number(options.limit) : 50;
1052
+ const filter = options?.filter ? options.filter : void 0;
1053
+ const activity = linearService.getActivityLog(limit, filter);
1054
+ logger9.info(`Retrieved ${activity.length} Linear activity items`);
1055
+ return {
1056
+ success: true,
1057
+ data: {
1058
+ activity,
1059
+ count: activity.length
1060
+ },
1061
+ metadata: {
1062
+ activityCount: activity.length
1063
+ }
1064
+ };
1065
+ } catch (error) {
1066
+ logger9.error("Failed to get Linear activity:", error);
1067
+ return {
1068
+ success: false,
1069
+ error: error instanceof Error ? error.message : "Failed to get activity"
1070
+ };
1071
+ }
1072
+ },
1073
+ examples: [
1074
+ {
1075
+ input: "Show me recent Linear activity",
1076
+ output: "Recent activity:\n1. Created issue ENG-123\n2. Updated issue BUG-456\n3. Added comment to FEAT-789...",
1077
+ explanation: "Shows recent Linear operations performed by the agent"
1078
+ }
1079
+ ]
1080
+ };
1081
+
1082
+ // src/actions/clearActivity.ts
1083
+ import {
1084
+ logger as logger10
1085
+ } from "@elizaos/core";
1086
+ var clearLinearActivityAction = {
1087
+ name: "CLEAR_LINEAR_ACTIVITY",
1088
+ description: "Clear the Linear activity log",
1089
+ similes: ["clear activity", "reset activity", "delete activity log"],
1090
+ async validate(runtime, _message, state) {
1091
+ try {
1092
+ const linearService = runtime.getService("linear");
1093
+ return !!linearService;
1094
+ } catch {
1095
+ return false;
1096
+ }
1097
+ },
1098
+ async handler(runtime, message, state, options) {
1099
+ try {
1100
+ const linearService = runtime.getService("linear");
1101
+ if (!linearService) {
1102
+ throw new Error("Linear service not available");
1103
+ }
1104
+ linearService.clearActivityLog();
1105
+ logger10.info("Cleared Linear activity log");
1106
+ return {
1107
+ success: true,
1108
+ data: {
1109
+ message: "Activity log cleared successfully"
1110
+ }
1111
+ };
1112
+ } catch (error) {
1113
+ logger10.error("Failed to clear Linear activity:", error);
1114
+ return {
1115
+ success: false,
1116
+ error: error instanceof Error ? error.message : "Failed to clear activity"
1117
+ };
1118
+ }
1119
+ },
1120
+ examples: [
1121
+ {
1122
+ input: "Clear the Linear activity log",
1123
+ output: "Linear activity log has been cleared",
1124
+ explanation: "Clears all stored activity history"
1125
+ }
1126
+ ]
1127
+ };
1128
+
1129
+ // src/providers/issues.ts
1130
+ var linearIssuesProvider = {
1131
+ name: "LINEAR_ISSUES",
1132
+ description: "Provides context about recent Linear issues",
1133
+ get: async (runtime, _message, _state) => {
1134
+ try {
1135
+ const linearService = runtime.getService("linear");
1136
+ if (!linearService) {
1137
+ return {
1138
+ text: "Linear service is not available"
1139
+ };
1140
+ }
1141
+ const issues = await linearService.searchIssues({ limit: 10 });
1142
+ if (issues.length === 0) {
1143
+ return {
1144
+ text: "No recent Linear issues found"
1145
+ };
1146
+ }
1147
+ const issuesList = await Promise.all(
1148
+ issues.map(async (issue) => {
1149
+ const [assignee, state] = await Promise.all([
1150
+ issue.assignee,
1151
+ issue.state
1152
+ ]);
1153
+ return `- ${issue.identifier}: ${issue.title} (${state?.name || "Unknown"}, ${assignee?.name || "Unassigned"})`;
1154
+ })
1155
+ );
1156
+ const text = `Recent Linear Issues:
1157
+ ${issuesList.join("\n")}`;
1158
+ return {
1159
+ text,
1160
+ data: {
1161
+ issues: issues.map((issue) => ({
1162
+ id: issue.id,
1163
+ identifier: issue.identifier,
1164
+ title: issue.title
1165
+ }))
1166
+ }
1167
+ };
1168
+ } catch (error) {
1169
+ return {
1170
+ text: "Error retrieving Linear issues"
1171
+ };
1172
+ }
1173
+ }
1174
+ };
1175
+
1176
+ // src/providers/teams.ts
1177
+ var linearTeamsProvider = {
1178
+ name: "LINEAR_TEAMS",
1179
+ description: "Provides context about Linear teams",
1180
+ get: async (runtime, _message, _state) => {
1181
+ try {
1182
+ const linearService = runtime.getService("linear");
1183
+ if (!linearService) {
1184
+ return {
1185
+ text: "Linear service is not available"
1186
+ };
1187
+ }
1188
+ const teams = await linearService.getTeams();
1189
+ if (teams.length === 0) {
1190
+ return {
1191
+ text: "No Linear teams found"
1192
+ };
1193
+ }
1194
+ const teamsList = teams.map(
1195
+ (team) => `- ${team.name} (${team.key}): ${team.description || "No description"}`
1196
+ );
1197
+ const text = `Linear Teams:
1198
+ ${teamsList.join("\n")}`;
1199
+ return {
1200
+ text,
1201
+ data: {
1202
+ teams: teams.map((team) => ({
1203
+ id: team.id,
1204
+ name: team.name,
1205
+ key: team.key
1206
+ }))
1207
+ }
1208
+ };
1209
+ } catch (error) {
1210
+ return {
1211
+ text: "Error retrieving Linear teams"
1212
+ };
1213
+ }
1214
+ }
1215
+ };
1216
+
1217
+ // src/providers/projects.ts
1218
+ var linearProjectsProvider = {
1219
+ name: "LINEAR_PROJECTS",
1220
+ description: "Provides context about active Linear projects",
1221
+ get: async (runtime, _message, _state) => {
1222
+ try {
1223
+ const linearService = runtime.getService("linear");
1224
+ if (!linearService) {
1225
+ return {
1226
+ text: "Linear service is not available"
1227
+ };
1228
+ }
1229
+ const projects = await linearService.getProjects();
1230
+ if (projects.length === 0) {
1231
+ return {
1232
+ text: "No Linear projects found"
1233
+ };
1234
+ }
1235
+ const activeProjects = projects.filter(
1236
+ (project) => project.state === "started" || project.state === "planned"
1237
+ );
1238
+ const projectsList = activeProjects.slice(0, 10).map(
1239
+ (project) => `- ${project.name}: ${project.state} (${project.startDate || "No start date"} - ${project.targetDate || "No target date"})`
1240
+ );
1241
+ const text = `Active Linear Projects:
1242
+ ${projectsList.join("\n")}`;
1243
+ return {
1244
+ text,
1245
+ data: {
1246
+ projects: activeProjects.slice(0, 10).map((project) => ({
1247
+ id: project.id,
1248
+ name: project.name,
1249
+ state: project.state
1250
+ }))
1251
+ }
1252
+ };
1253
+ } catch (error) {
1254
+ return {
1255
+ text: "Error retrieving Linear projects"
1256
+ };
1257
+ }
1258
+ }
1259
+ };
1260
+
1261
+ // src/providers/activity.ts
1262
+ var linearActivityProvider = {
1263
+ name: "LINEAR_ACTIVITY",
1264
+ description: "Provides context about recent Linear activity",
1265
+ get: async (runtime, _message, _state) => {
1266
+ try {
1267
+ const linearService = runtime.getService("linear");
1268
+ if (!linearService) {
1269
+ return {
1270
+ text: "Linear service is not available"
1271
+ };
1272
+ }
1273
+ const activity = linearService.getActivityLog(10);
1274
+ if (activity.length === 0) {
1275
+ return {
1276
+ text: "No recent Linear activity"
1277
+ };
1278
+ }
1279
+ const activityList = activity.map((item) => {
1280
+ const status = item.success ? "\u2713" : "\u2717";
1281
+ const time = new Date(item.timestamp).toLocaleTimeString();
1282
+ return `${status} ${time}: ${item.action} ${item.resource_type} ${item.resource_id}`;
1283
+ });
1284
+ const text = `Recent Linear Activity:
1285
+ ${activityList.join("\n")}`;
1286
+ return {
1287
+ text,
1288
+ data: {
1289
+ activity: activity.slice(0, 10)
1290
+ }
1291
+ };
1292
+ } catch (error) {
1293
+ return {
1294
+ text: "Error retrieving Linear activity"
1295
+ };
1296
+ }
1297
+ }
1298
+ };
1299
+
1300
+ // src/index.ts
1301
+ var linearPlugin = {
1302
+ name: "linear",
1303
+ description: "Linear integration plugin for issue tracking and project management",
1304
+ services: [LinearService],
1305
+ actions: [
1306
+ createLinearIssueAction,
1307
+ getLinearIssueAction,
1308
+ updateLinearIssueAction,
1309
+ searchLinearIssuesAction,
1310
+ createLinearCommentAction,
1311
+ listLinearTeamsAction,
1312
+ listLinearProjectsAction,
1313
+ getLinearActivityAction,
1314
+ clearLinearActivityAction
1315
+ ],
1316
+ providers: [
1317
+ linearIssuesProvider,
1318
+ linearTeamsProvider,
1319
+ linearProjectsProvider,
1320
+ linearActivityProvider
1321
+ ],
1322
+ // No evaluators or events for this plugin
1323
+ evaluators: [],
1324
+ events: {}
1325
+ };
1326
+ export {
1327
+ LinearAPIError,
1328
+ LinearAuthenticationError,
1329
+ LinearRateLimitError,
1330
+ LinearService,
1331
+ linearPlugin
1332
+ };
1333
+ //# sourceMappingURL=index.js.map