@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/README.md +303 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.js +1333 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
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
|