@elizaos/plugin-linear 1.2.5 → 1.2.7
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 +739 -500
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -360,770 +360,1012 @@ var LinearService = _LinearService;
|
|
|
360
360
|
|
|
361
361
|
// src/actions/createIssue.ts
|
|
362
362
|
import {
|
|
363
|
+
ModelType,
|
|
363
364
|
logger as logger2
|
|
364
365
|
} from "@elizaos/core";
|
|
365
|
-
var createIssueTemplate = `
|
|
366
|
+
var createIssueTemplate = `Given the user's request, extract the information needed to create a Linear issue.
|
|
366
367
|
|
|
367
|
-
|
|
368
|
-
{{recentMessages}}
|
|
368
|
+
User request: "{{userMessage}}"
|
|
369
369
|
|
|
370
|
-
|
|
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
|
|
370
|
+
Extract and return ONLY a JSON object (no markdown formatting, no code blocks) with the following structure:
|
|
379
371
|
{
|
|
380
|
-
"title": "
|
|
381
|
-
"description": "Detailed description
|
|
382
|
-
"
|
|
383
|
-
"priority": 3,
|
|
384
|
-
"
|
|
372
|
+
"title": "Brief, clear issue title",
|
|
373
|
+
"description": "Detailed description of the issue (optional)",
|
|
374
|
+
"teamKey": "Team key if mentioned (e.g., ENG, PROD)",
|
|
375
|
+
"priority": "Priority level if mentioned (1=urgent, 2=high, 3=normal, 4=low)",
|
|
376
|
+
"labels": ["label1", "label2"] (if any labels are mentioned),
|
|
377
|
+
"assignee": "Assignee username or email if mentioned"
|
|
385
378
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
var
|
|
379
|
+
|
|
380
|
+
Return only the JSON object, no other text.`;
|
|
381
|
+
var createIssueAction = {
|
|
389
382
|
name: "CREATE_LINEAR_ISSUE",
|
|
390
383
|
description: "Create a new issue in Linear",
|
|
391
|
-
similes: ["create
|
|
392
|
-
|
|
384
|
+
similes: ["create-linear-issue", "new-linear-issue", "add-linear-issue"],
|
|
385
|
+
examples: [[
|
|
386
|
+
{
|
|
387
|
+
name: "User",
|
|
388
|
+
content: {
|
|
389
|
+
text: "Create a new issue: Fix login button not working on mobile devices"
|
|
390
|
+
}
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
name: "Assistant",
|
|
394
|
+
content: {
|
|
395
|
+
text: "I'll create that issue for you in Linear.",
|
|
396
|
+
actions: ["CREATE_LINEAR_ISSUE"]
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
], [
|
|
400
|
+
{
|
|
401
|
+
name: "User",
|
|
402
|
+
content: {
|
|
403
|
+
text: "Create a bug report for the ENG team: API returns 500 error when updating user profile"
|
|
404
|
+
}
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
name: "Assistant",
|
|
408
|
+
content: {
|
|
409
|
+
text: "I'll create a bug report for the engineering team right away.",
|
|
410
|
+
actions: ["CREATE_LINEAR_ISSUE"]
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
]],
|
|
414
|
+
async validate(runtime, _message, _state) {
|
|
393
415
|
try {
|
|
394
|
-
const
|
|
395
|
-
return !!
|
|
416
|
+
const apiKey = runtime.getSetting("LINEAR_API_KEY");
|
|
417
|
+
return !!apiKey;
|
|
396
418
|
} catch {
|
|
397
419
|
return false;
|
|
398
420
|
}
|
|
399
421
|
},
|
|
400
|
-
async handler(runtime, message,
|
|
422
|
+
async handler(runtime, message, _state, _options) {
|
|
401
423
|
try {
|
|
402
424
|
const linearService = runtime.getService("linear");
|
|
403
425
|
if (!linearService) {
|
|
404
426
|
throw new Error("Linear service not available");
|
|
405
427
|
}
|
|
406
|
-
|
|
407
|
-
|
|
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);
|
|
428
|
+
const content = message.content.text;
|
|
429
|
+
if (!content) {
|
|
417
430
|
return {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
431
|
+
text: "Please provide a description for the issue.",
|
|
432
|
+
success: false
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
const structuredData = _options?.issueData;
|
|
436
|
+
let issueData;
|
|
437
|
+
if (structuredData) {
|
|
438
|
+
issueData = structuredData;
|
|
439
|
+
} else {
|
|
440
|
+
const prompt = createIssueTemplate.replace("{{userMessage}}", content);
|
|
441
|
+
const response = await runtime.useModel(ModelType.TEXT_LARGE, {
|
|
442
|
+
prompt
|
|
443
|
+
});
|
|
444
|
+
if (!response) {
|
|
445
|
+
throw new Error("Failed to extract issue information");
|
|
446
|
+
}
|
|
447
|
+
try {
|
|
448
|
+
const cleanedResponse = response.replace(/^```(?:json)?\n?/, "").replace(/\n?```$/, "").trim();
|
|
449
|
+
const parsed = JSON.parse(cleanedResponse);
|
|
450
|
+
issueData = {
|
|
451
|
+
title: parsed.title,
|
|
452
|
+
description: parsed.description,
|
|
453
|
+
priority: parsed.priority
|
|
454
|
+
};
|
|
455
|
+
if (parsed.teamKey) {
|
|
456
|
+
const teams = await linearService.getTeams();
|
|
457
|
+
const team = teams.find(
|
|
458
|
+
(t) => t.key.toLowerCase() === parsed.teamKey.toLowerCase()
|
|
459
|
+
);
|
|
460
|
+
if (team) {
|
|
461
|
+
issueData.teamId = team.id;
|
|
425
462
|
}
|
|
426
|
-
},
|
|
427
|
-
metadata: {
|
|
428
|
-
issueId: issue2.id,
|
|
429
|
-
identifier: issue2.identifier
|
|
430
463
|
}
|
|
431
|
-
|
|
464
|
+
if (parsed.assignee) {
|
|
465
|
+
const users = await linearService.getUsers();
|
|
466
|
+
const user = users.find(
|
|
467
|
+
(u) => u.email === parsed.assignee || u.name.toLowerCase().includes(parsed.assignee.toLowerCase())
|
|
468
|
+
);
|
|
469
|
+
if (user) {
|
|
470
|
+
issueData.assigneeId = user.id;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
if (!issueData.teamId) {
|
|
474
|
+
const teams = await linearService.getTeams();
|
|
475
|
+
if (teams.length > 0) {
|
|
476
|
+
issueData.teamId = teams[0].id;
|
|
477
|
+
logger2.warn(`No team specified, using default team: ${teams[0].name}`);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
} catch (parseError) {
|
|
481
|
+
logger2.error("Failed to parse LLM response:", parseError);
|
|
482
|
+
issueData = {
|
|
483
|
+
title: content.length > 100 ? content.substring(0, 100) + "..." : content,
|
|
484
|
+
description: content
|
|
485
|
+
};
|
|
486
|
+
const teams = await linearService.getTeams();
|
|
487
|
+
if (teams.length > 0) {
|
|
488
|
+
issueData.teamId = teams[0].id;
|
|
489
|
+
logger2.warn(`Using default team for fallback: ${teams[0].name}`);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
432
492
|
}
|
|
433
|
-
|
|
434
|
-
messages: state.messages || [],
|
|
435
|
-
context: createIssueTemplate
|
|
436
|
-
});
|
|
437
|
-
const parsed = JSON.parse(response.trim().replace(/```json\n?|\n?```/g, ""));
|
|
438
|
-
if (!parsed.shouldCreate) {
|
|
493
|
+
if (!issueData.title) {
|
|
439
494
|
return {
|
|
440
|
-
|
|
441
|
-
|
|
495
|
+
text: "Could not determine issue title. Please provide more details.",
|
|
496
|
+
success: false
|
|
442
497
|
};
|
|
443
498
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
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
|
-
}
|
|
499
|
+
if (!issueData.teamId) {
|
|
500
|
+
return {
|
|
501
|
+
text: "No Linear teams found. Please ensure at least one team exists in your Linear workspace.",
|
|
502
|
+
success: false
|
|
503
|
+
};
|
|
459
504
|
}
|
|
460
|
-
const
|
|
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}`);
|
|
505
|
+
const issue = await linearService.createIssue(issueData);
|
|
468
506
|
return {
|
|
507
|
+
text: `Created issue: ${issue.title} (${issue.identifier})`,
|
|
469
508
|
success: true,
|
|
470
509
|
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
510
|
issueId: issue.id,
|
|
481
|
-
identifier: issue.identifier
|
|
511
|
+
identifier: issue.identifier,
|
|
512
|
+
url: issue.url
|
|
482
513
|
}
|
|
483
514
|
};
|
|
484
515
|
} catch (error) {
|
|
485
|
-
logger2.error("Failed to create
|
|
516
|
+
logger2.error("Failed to create issue:", error);
|
|
486
517
|
return {
|
|
487
|
-
|
|
488
|
-
|
|
518
|
+
text: `Failed to create issue: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
519
|
+
success: false
|
|
489
520
|
};
|
|
490
521
|
}
|
|
491
|
-
}
|
|
492
|
-
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
// src/actions/getIssue.ts
|
|
526
|
+
import { logger as logger3 } from "@elizaos/core";
|
|
527
|
+
var getIssueAction = {
|
|
528
|
+
name: "GET_LINEAR_ISSUE",
|
|
529
|
+
description: "Get details of a specific Linear issue",
|
|
530
|
+
similes: ["get-linear-issue", "show-linear-issue", "view-linear-issue"],
|
|
531
|
+
examples: [[
|
|
493
532
|
{
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
533
|
+
name: "User",
|
|
534
|
+
content: {
|
|
535
|
+
text: "Show me issue ENG-123"
|
|
536
|
+
}
|
|
497
537
|
},
|
|
498
538
|
{
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
539
|
+
name: "Assistant",
|
|
540
|
+
content: {
|
|
541
|
+
text: "I'll get the details for issue ENG-123.",
|
|
542
|
+
actions: ["GET_LINEAR_ISSUE"]
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
], [
|
|
546
|
+
{
|
|
547
|
+
name: "User",
|
|
548
|
+
content: {
|
|
549
|
+
text: "What's the status of BUG-456?"
|
|
550
|
+
}
|
|
502
551
|
},
|
|
503
552
|
{
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
553
|
+
name: "Assistant",
|
|
554
|
+
content: {
|
|
555
|
+
text: "Let me check the status of BUG-456 for you.",
|
|
556
|
+
actions: ["GET_LINEAR_ISSUE"]
|
|
557
|
+
}
|
|
507
558
|
}
|
|
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) {
|
|
559
|
+
]],
|
|
560
|
+
async validate(runtime, _message, _state) {
|
|
520
561
|
try {
|
|
521
|
-
const
|
|
522
|
-
return !!
|
|
562
|
+
const apiKey = runtime.getSetting("LINEAR_API_KEY");
|
|
563
|
+
return !!apiKey;
|
|
523
564
|
} catch {
|
|
524
565
|
return false;
|
|
525
566
|
}
|
|
526
567
|
},
|
|
527
|
-
async handler(runtime, message,
|
|
568
|
+
async handler(runtime, message, _state, _options) {
|
|
528
569
|
try {
|
|
529
570
|
const linearService = runtime.getService("linear");
|
|
530
571
|
if (!linearService) {
|
|
531
572
|
throw new Error("Linear service not available");
|
|
532
573
|
}
|
|
533
|
-
|
|
534
|
-
if (
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
if (match) {
|
|
540
|
-
issueId = match[0];
|
|
541
|
-
}
|
|
574
|
+
const content = message.content.text;
|
|
575
|
+
if (!content) {
|
|
576
|
+
return {
|
|
577
|
+
text: "Please specify an issue ID.",
|
|
578
|
+
success: false
|
|
579
|
+
};
|
|
542
580
|
}
|
|
543
|
-
|
|
581
|
+
const issueMatch = content.match(/(\w+-\d+)/);
|
|
582
|
+
if (!issueMatch) {
|
|
544
583
|
return {
|
|
545
|
-
|
|
546
|
-
|
|
584
|
+
text: "Please provide a valid issue ID (e.g., ENG-123).",
|
|
585
|
+
success: false
|
|
547
586
|
};
|
|
548
587
|
}
|
|
588
|
+
const issueId = issueMatch[1];
|
|
549
589
|
const issue = await linearService.getIssue(issueId);
|
|
550
|
-
const
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
const labelList = await labels.nodes;
|
|
557
|
-
const issueData = {
|
|
590
|
+
const assignee = await issue.assignee;
|
|
591
|
+
const state = await issue.state;
|
|
592
|
+
const team = await issue.team;
|
|
593
|
+
const labels = await issue.labels();
|
|
594
|
+
const project = await issue.project;
|
|
595
|
+
const issueDetails = {
|
|
558
596
|
id: issue.id,
|
|
559
597
|
identifier: issue.identifier,
|
|
560
598
|
title: issue.title,
|
|
561
599
|
description: issue.description,
|
|
562
|
-
url: issue.url,
|
|
563
600
|
priority: issue.priority,
|
|
564
601
|
priorityLabel: issue.priorityLabel,
|
|
565
|
-
|
|
602
|
+
url: issue.url,
|
|
566
603
|
createdAt: issue.createdAt,
|
|
567
604
|
updatedAt: issue.updatedAt,
|
|
568
605
|
dueDate: issue.dueDate,
|
|
606
|
+
estimate: issue.estimate,
|
|
569
607
|
assignee: assignee ? {
|
|
570
608
|
id: assignee.id,
|
|
571
609
|
name: assignee.name,
|
|
572
610
|
email: assignee.email
|
|
573
611
|
} : null,
|
|
574
|
-
state: {
|
|
575
|
-
id:
|
|
576
|
-
name:
|
|
577
|
-
type:
|
|
578
|
-
color:
|
|
579
|
-
},
|
|
580
|
-
team: {
|
|
612
|
+
state: state ? {
|
|
613
|
+
id: state.id,
|
|
614
|
+
name: state.name,
|
|
615
|
+
type: state.type,
|
|
616
|
+
color: state.color
|
|
617
|
+
} : null,
|
|
618
|
+
team: team ? {
|
|
581
619
|
id: team.id,
|
|
582
620
|
name: team.name,
|
|
583
621
|
key: team.key
|
|
584
|
-
},
|
|
585
|
-
labels:
|
|
622
|
+
} : null,
|
|
623
|
+
labels: labels.nodes.map((label) => ({
|
|
586
624
|
id: label.id,
|
|
587
625
|
name: label.name,
|
|
588
626
|
color: label.color
|
|
589
|
-
}))
|
|
627
|
+
})),
|
|
628
|
+
project: project ? {
|
|
629
|
+
id: project.id,
|
|
630
|
+
name: project.name
|
|
631
|
+
} : null
|
|
590
632
|
};
|
|
591
|
-
|
|
633
|
+
let responseText = `Issue ${issue.identifier}: ${issue.title}
|
|
634
|
+
`;
|
|
635
|
+
responseText += `Status: ${state?.name || "Unknown"}
|
|
636
|
+
`;
|
|
637
|
+
responseText += `Priority: ${issue.priorityLabel}
|
|
638
|
+
`;
|
|
639
|
+
if (assignee) {
|
|
640
|
+
responseText += `Assignee: ${assignee.name}
|
|
641
|
+
`;
|
|
642
|
+
}
|
|
643
|
+
if (issue.dueDate) {
|
|
644
|
+
responseText += `Due: ${new Date(issue.dueDate).toLocaleDateString()}
|
|
645
|
+
`;
|
|
646
|
+
}
|
|
647
|
+
if (issue.description) {
|
|
648
|
+
responseText += `
|
|
649
|
+
Description: ${issue.description}
|
|
650
|
+
`;
|
|
651
|
+
}
|
|
652
|
+
responseText += `
|
|
653
|
+
View in Linear: ${issue.url}`;
|
|
592
654
|
return {
|
|
655
|
+
text: responseText,
|
|
593
656
|
success: true,
|
|
594
|
-
data:
|
|
595
|
-
issue: issueData
|
|
596
|
-
},
|
|
597
|
-
metadata: {
|
|
598
|
-
issueId: issue.id,
|
|
599
|
-
identifier: issue.identifier
|
|
600
|
-
}
|
|
657
|
+
data: issueDetails
|
|
601
658
|
};
|
|
602
659
|
} catch (error) {
|
|
603
|
-
logger3.error("Failed to get
|
|
660
|
+
logger3.error("Failed to get issue:", error);
|
|
604
661
|
return {
|
|
605
|
-
|
|
606
|
-
|
|
662
|
+
text: `Failed to get issue: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
663
|
+
success: false
|
|
607
664
|
};
|
|
608
665
|
}
|
|
609
|
-
}
|
|
610
|
-
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
// src/actions/updateIssue.ts
|
|
670
|
+
import { logger as logger4 } from "@elizaos/core";
|
|
671
|
+
var updateIssueAction = {
|
|
672
|
+
name: "UPDATE_LINEAR_ISSUE",
|
|
673
|
+
description: "Update an existing Linear issue",
|
|
674
|
+
similes: ["update-linear-issue", "edit-linear-issue", "modify-linear-issue"],
|
|
675
|
+
examples: [[
|
|
611
676
|
{
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
677
|
+
name: "User",
|
|
678
|
+
content: {
|
|
679
|
+
text: 'Update issue ENG-123 title to "Fix login button on all devices"'
|
|
680
|
+
}
|
|
615
681
|
},
|
|
616
682
|
{
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
683
|
+
name: "Assistant",
|
|
684
|
+
content: {
|
|
685
|
+
text: "I'll update the title of issue ENG-123 for you.",
|
|
686
|
+
actions: ["UPDATE_LINEAR_ISSUE"]
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
], [
|
|
690
|
+
{
|
|
691
|
+
name: "User",
|
|
692
|
+
content: {
|
|
693
|
+
text: "Change the priority of BUG-456 to high"
|
|
694
|
+
}
|
|
620
695
|
},
|
|
621
696
|
{
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
697
|
+
name: "Assistant",
|
|
698
|
+
content: {
|
|
699
|
+
text: "I'll change the priority of BUG-456 to high.",
|
|
700
|
+
actions: ["UPDATE_LINEAR_ISSUE"]
|
|
701
|
+
}
|
|
625
702
|
}
|
|
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) {
|
|
703
|
+
]],
|
|
704
|
+
async validate(runtime, _message, _state) {
|
|
638
705
|
try {
|
|
639
|
-
const
|
|
640
|
-
return !!
|
|
706
|
+
const apiKey = runtime.getSetting("LINEAR_API_KEY");
|
|
707
|
+
return !!apiKey;
|
|
641
708
|
} catch {
|
|
642
709
|
return false;
|
|
643
710
|
}
|
|
644
711
|
},
|
|
645
|
-
async handler(runtime, message,
|
|
712
|
+
async handler(runtime, message, _state, _options) {
|
|
646
713
|
try {
|
|
647
714
|
const linearService = runtime.getService("linear");
|
|
648
715
|
if (!linearService) {
|
|
649
716
|
throw new Error("Linear service not available");
|
|
650
717
|
}
|
|
651
|
-
const
|
|
652
|
-
if (!
|
|
718
|
+
const content = message.content.text;
|
|
719
|
+
if (!content) {
|
|
653
720
|
return {
|
|
654
|
-
|
|
655
|
-
|
|
721
|
+
text: "Please provide update instructions for the issue.",
|
|
722
|
+
success: false
|
|
656
723
|
};
|
|
657
724
|
}
|
|
725
|
+
const issueMatch = content.match(/(\w+-\d+)/);
|
|
726
|
+
if (!issueMatch) {
|
|
727
|
+
return {
|
|
728
|
+
text: "Please specify an issue ID (e.g., ENG-123) to update.",
|
|
729
|
+
success: false
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
const issueId = issueMatch[1];
|
|
658
733
|
const updates = {};
|
|
659
|
-
|
|
660
|
-
if (
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
if (
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
734
|
+
const titleMatch = content.match(/title to ["'](.+?)["']/i);
|
|
735
|
+
if (titleMatch) {
|
|
736
|
+
updates.title = titleMatch[1];
|
|
737
|
+
}
|
|
738
|
+
const priorityMatch = content.match(/priority (?:to |as )?(\w+)/i);
|
|
739
|
+
if (priorityMatch) {
|
|
740
|
+
const priorityMap = {
|
|
741
|
+
"urgent": 1,
|
|
742
|
+
"high": 2,
|
|
743
|
+
"normal": 3,
|
|
744
|
+
"medium": 3,
|
|
745
|
+
"low": 4
|
|
746
|
+
};
|
|
747
|
+
const priority = priorityMap[priorityMatch[1].toLowerCase()];
|
|
748
|
+
if (priority) {
|
|
749
|
+
updates.priority = priority;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
const descMatch = content.match(/description to ["'](.+?)["']/i);
|
|
753
|
+
if (descMatch) {
|
|
754
|
+
updates.description = descMatch[1];
|
|
755
|
+
}
|
|
756
|
+
const statusMatch = content.match(/status to (\w+)/i);
|
|
757
|
+
if (statusMatch) {
|
|
758
|
+
logger4.warn("Status updates not yet implemented");
|
|
759
|
+
}
|
|
760
|
+
if (Object.keys(updates).length === 0) {
|
|
761
|
+
return {
|
|
762
|
+
text: `No valid updates found. Please specify what to update (e.g., "Update issue ENG-123 title to 'New Title'")`,
|
|
763
|
+
success: false
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
const updatedIssue = await linearService.updateIssue(issueId, updates);
|
|
767
|
+
const updateSummary = Object.entries(updates).map(([key, value]) => `${key}: ${value}`).join(", ");
|
|
670
768
|
return {
|
|
769
|
+
text: `Updated issue ${updatedIssue.identifier}: ${updateSummary}`,
|
|
671
770
|
success: true,
|
|
672
771
|
data: {
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
url: issue.url
|
|
678
|
-
}
|
|
679
|
-
},
|
|
680
|
-
metadata: {
|
|
681
|
-
issueId: issue.id,
|
|
682
|
-
identifier: issue.identifier,
|
|
683
|
-
updates: Object.keys(updates)
|
|
772
|
+
issueId: updatedIssue.id,
|
|
773
|
+
identifier: updatedIssue.identifier,
|
|
774
|
+
updates,
|
|
775
|
+
url: updatedIssue.url
|
|
684
776
|
}
|
|
685
777
|
};
|
|
686
778
|
} catch (error) {
|
|
687
|
-
logger4.error("Failed to update
|
|
779
|
+
logger4.error("Failed to update issue:", error);
|
|
688
780
|
return {
|
|
689
|
-
|
|
690
|
-
|
|
781
|
+
text: `Failed to update issue: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
782
|
+
success: false
|
|
691
783
|
};
|
|
692
784
|
}
|
|
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
|
-
]
|
|
785
|
+
}
|
|
701
786
|
};
|
|
702
787
|
|
|
703
788
|
// src/actions/searchIssues.ts
|
|
704
789
|
import {
|
|
790
|
+
ModelType as ModelType2,
|
|
705
791
|
logger as logger5
|
|
706
792
|
} from "@elizaos/core";
|
|
707
|
-
var
|
|
708
|
-
|
|
709
|
-
Recent conversation:
|
|
710
|
-
{{recentMessages}}
|
|
793
|
+
var searchTemplate = `Extract search criteria from the user's request for Linear issues.
|
|
711
794
|
|
|
712
|
-
|
|
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
|
|
795
|
+
User request: "{{userMessage}}"
|
|
720
796
|
|
|
721
|
-
|
|
722
|
-
\`\`\`json
|
|
797
|
+
Extract and return ONLY a JSON object (no markdown formatting, no code blocks) with these possible filters:
|
|
723
798
|
{
|
|
724
|
-
"query": "search text
|
|
725
|
-
"state":
|
|
726
|
-
"assignee":
|
|
727
|
-
"
|
|
728
|
-
"
|
|
729
|
-
"
|
|
730
|
-
"
|
|
731
|
-
"limit":
|
|
799
|
+
"query": "general search text",
|
|
800
|
+
"state": "filter by state name (e.g., 'In Progress', 'Done', 'Todo')",
|
|
801
|
+
"assignee": "filter by assignee name or email",
|
|
802
|
+
"priority": "filter by priority (1=urgent, 2=high, 3=normal, 4=low)",
|
|
803
|
+
"team": "filter by team name or key",
|
|
804
|
+
"label": "filter by label name",
|
|
805
|
+
"hasAssignee": true/false - whether issue should have an assignee,
|
|
806
|
+
"limit": number of results to return (default 10)
|
|
732
807
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
var
|
|
808
|
+
|
|
809
|
+
Only include fields that are mentioned. Return only the JSON object.`;
|
|
810
|
+
var searchIssuesAction = {
|
|
736
811
|
name: "SEARCH_LINEAR_ISSUES",
|
|
737
|
-
description: "Search for issues in Linear
|
|
738
|
-
similes: ["search
|
|
739
|
-
|
|
812
|
+
description: "Search for issues in Linear with various filters",
|
|
813
|
+
similes: ["search-linear-issues", "find-linear-issues", "query-linear-issues"],
|
|
814
|
+
examples: [[
|
|
815
|
+
{
|
|
816
|
+
name: "User",
|
|
817
|
+
content: {
|
|
818
|
+
text: "Show me all open bugs"
|
|
819
|
+
}
|
|
820
|
+
},
|
|
821
|
+
{
|
|
822
|
+
name: "Assistant",
|
|
823
|
+
content: {
|
|
824
|
+
text: "I'll search for all open bug issues in Linear.",
|
|
825
|
+
actions: ["SEARCH_LINEAR_ISSUES"]
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
], [
|
|
829
|
+
{
|
|
830
|
+
name: "User",
|
|
831
|
+
content: {
|
|
832
|
+
text: "Find high priority issues assigned to me"
|
|
833
|
+
}
|
|
834
|
+
},
|
|
835
|
+
{
|
|
836
|
+
name: "Assistant",
|
|
837
|
+
content: {
|
|
838
|
+
text: "I'll search for high priority issues assigned to you.",
|
|
839
|
+
actions: ["SEARCH_LINEAR_ISSUES"]
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
]],
|
|
843
|
+
async validate(runtime, _message, _state) {
|
|
740
844
|
try {
|
|
741
|
-
const
|
|
742
|
-
return !!
|
|
845
|
+
const apiKey = runtime.getSetting("LINEAR_API_KEY");
|
|
846
|
+
return !!apiKey;
|
|
743
847
|
} catch {
|
|
744
848
|
return false;
|
|
745
849
|
}
|
|
746
850
|
},
|
|
747
|
-
async handler(runtime, message,
|
|
851
|
+
async handler(runtime, message, _state, _options) {
|
|
748
852
|
try {
|
|
749
853
|
const linearService = runtime.getService("linear");
|
|
750
854
|
if (!linearService) {
|
|
751
855
|
throw new Error("Linear service not available");
|
|
752
856
|
}
|
|
753
|
-
|
|
754
|
-
if (
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
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
|
|
857
|
+
const content = message.content.text;
|
|
858
|
+
if (!content) {
|
|
859
|
+
return {
|
|
860
|
+
text: "Please provide search criteria for issues.",
|
|
861
|
+
success: false
|
|
764
862
|
};
|
|
863
|
+
}
|
|
864
|
+
let filters = {};
|
|
865
|
+
if (_options?.filters) {
|
|
866
|
+
filters = _options.filters;
|
|
765
867
|
} else {
|
|
766
|
-
const
|
|
767
|
-
|
|
768
|
-
|
|
868
|
+
const prompt = searchTemplate.replace("{{userMessage}}", content);
|
|
869
|
+
const response = await runtime.useModel(ModelType2.TEXT_LARGE, {
|
|
870
|
+
prompt
|
|
769
871
|
});
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
872
|
+
if (!response) {
|
|
873
|
+
filters = { query: content };
|
|
874
|
+
} else {
|
|
875
|
+
try {
|
|
876
|
+
const cleanedResponse = response.replace(/^```(?:json)?\n?/, "").replace(/\n?```$/, "").trim();
|
|
877
|
+
const parsed = JSON.parse(cleanedResponse);
|
|
878
|
+
filters = {
|
|
879
|
+
query: parsed.query,
|
|
880
|
+
state: parsed.state ? [parsed.state] : void 0,
|
|
881
|
+
assignee: parsed.assignee ? [parsed.assignee] : void 0,
|
|
882
|
+
priority: parsed.priority ? [parsed.priority] : void 0,
|
|
883
|
+
team: parsed.team,
|
|
884
|
+
label: parsed.label ? [parsed.label] : void 0,
|
|
885
|
+
limit: parsed.limit
|
|
886
|
+
};
|
|
887
|
+
Object.keys(filters).forEach((key) => {
|
|
888
|
+
if (filters[key] === void 0) {
|
|
889
|
+
delete filters[key];
|
|
890
|
+
}
|
|
891
|
+
});
|
|
892
|
+
} catch (parseError) {
|
|
893
|
+
logger5.error("Failed to parse search filters:", parseError);
|
|
894
|
+
filters = { query: content };
|
|
895
|
+
}
|
|
896
|
+
}
|
|
774
897
|
}
|
|
898
|
+
filters.limit = _options?.limit || 10;
|
|
775
899
|
const issues = await linearService.searchIssues(filters);
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
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`);
|
|
900
|
+
if (issues.length === 0) {
|
|
901
|
+
return {
|
|
902
|
+
text: "No issues found matching your search criteria.",
|
|
903
|
+
success: true,
|
|
904
|
+
data: {
|
|
905
|
+
issues: [],
|
|
906
|
+
filters,
|
|
907
|
+
count: 0
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
const issueList = await Promise.all(issues.map(async (issue, index) => {
|
|
912
|
+
const state = await issue.state;
|
|
913
|
+
return `${index + 1}. ${issue.identifier}: ${issue.title} (${state?.name || "No state"})`;
|
|
914
|
+
}));
|
|
915
|
+
const issueText = issueList.join("\n");
|
|
799
916
|
return {
|
|
917
|
+
text: `Found ${issues.length} issue${issues.length === 1 ? "" : "s"}:
|
|
918
|
+
${issueText}`,
|
|
800
919
|
success: true,
|
|
801
920
|
data: {
|
|
802
|
-
issues:
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
921
|
+
issues: issues.map((i) => ({
|
|
922
|
+
id: i.id,
|
|
923
|
+
identifier: i.identifier,
|
|
924
|
+
title: i.title,
|
|
925
|
+
description: i.description,
|
|
926
|
+
url: i.url,
|
|
927
|
+
state: i.state,
|
|
928
|
+
priority: i.priority,
|
|
929
|
+
priorityLabel: i.priorityLabel,
|
|
930
|
+
assignee: i.assignee
|
|
931
|
+
})),
|
|
932
|
+
filters,
|
|
933
|
+
count: issues.length
|
|
809
934
|
}
|
|
810
935
|
};
|
|
811
936
|
} catch (error) {
|
|
812
|
-
logger5.error("Failed to search
|
|
937
|
+
logger5.error("Failed to search issues:", error);
|
|
813
938
|
return {
|
|
814
|
-
|
|
815
|
-
|
|
939
|
+
text: `Failed to search issues: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
940
|
+
success: false
|
|
816
941
|
};
|
|
817
942
|
}
|
|
818
|
-
}
|
|
819
|
-
|
|
943
|
+
}
|
|
944
|
+
};
|
|
945
|
+
|
|
946
|
+
// src/actions/createComment.ts
|
|
947
|
+
import { logger as logger6 } from "@elizaos/core";
|
|
948
|
+
var createCommentAction = {
|
|
949
|
+
name: "CREATE_LINEAR_COMMENT",
|
|
950
|
+
description: "Create a comment on a Linear issue",
|
|
951
|
+
similes: ["create-linear-comment", "add-linear-comment", "comment-on-linear-issue"],
|
|
952
|
+
examples: [[
|
|
820
953
|
{
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
954
|
+
name: "User",
|
|
955
|
+
content: {
|
|
956
|
+
text: "Comment on ENG-123: This has been fixed in the latest release"
|
|
957
|
+
}
|
|
824
958
|
},
|
|
825
959
|
{
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
960
|
+
name: "Assistant",
|
|
961
|
+
content: {
|
|
962
|
+
text: "I'll add that comment to issue ENG-123.",
|
|
963
|
+
actions: ["CREATE_LINEAR_COMMENT"]
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
], [
|
|
967
|
+
{
|
|
968
|
+
name: "User",
|
|
969
|
+
content: {
|
|
970
|
+
text: "Add a comment to BUG-456: Need more information from the reporter"
|
|
971
|
+
}
|
|
829
972
|
},
|
|
830
973
|
{
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
974
|
+
name: "Assistant",
|
|
975
|
+
content: {
|
|
976
|
+
text: "I'll post that comment on BUG-456 right away.",
|
|
977
|
+
actions: ["CREATE_LINEAR_COMMENT"]
|
|
978
|
+
}
|
|
834
979
|
}
|
|
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) {
|
|
980
|
+
]],
|
|
981
|
+
async validate(runtime, _message, _state) {
|
|
847
982
|
try {
|
|
848
|
-
const
|
|
849
|
-
return !!
|
|
983
|
+
const apiKey = runtime.getSetting("LINEAR_API_KEY");
|
|
984
|
+
return !!apiKey;
|
|
850
985
|
} catch {
|
|
851
986
|
return false;
|
|
852
987
|
}
|
|
853
988
|
},
|
|
854
|
-
async handler(runtime, message,
|
|
989
|
+
async handler(runtime, message, _state, _options) {
|
|
855
990
|
try {
|
|
856
991
|
const linearService = runtime.getService("linear");
|
|
857
992
|
if (!linearService) {
|
|
858
993
|
throw new Error("Linear service not available");
|
|
859
994
|
}
|
|
860
|
-
const
|
|
861
|
-
|
|
862
|
-
if (!issueId || !body) {
|
|
995
|
+
const content = message.content.text;
|
|
996
|
+
if (!content) {
|
|
863
997
|
return {
|
|
864
|
-
|
|
865
|
-
|
|
998
|
+
text: "Please provide a message with the issue ID and comment content.",
|
|
999
|
+
success: false
|
|
866
1000
|
};
|
|
867
1001
|
}
|
|
868
|
-
const
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
1002
|
+
const issueMatch = content.match(/(?:comment on|add.*comment.*to)\s+(\w+-\d+):?\s*(.*)/i);
|
|
1003
|
+
if (!issueMatch) {
|
|
1004
|
+
return {
|
|
1005
|
+
text: 'Please specify the issue ID and comment content. Example: "Comment on ENG-123: This looks good"',
|
|
1006
|
+
success: false
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
const [, issueIdentifier, commentBody] = issueMatch;
|
|
1010
|
+
const issue = await linearService.getIssue(issueIdentifier);
|
|
1011
|
+
const comment = await linearService.createComment({
|
|
1012
|
+
issueId: issue.id,
|
|
1013
|
+
body: commentBody.trim()
|
|
1014
|
+
});
|
|
874
1015
|
return {
|
|
1016
|
+
text: `Comment added to issue ${issueIdentifier}: "${commentBody.trim()}"`,
|
|
875
1017
|
success: true,
|
|
876
1018
|
data: {
|
|
877
|
-
comment: {
|
|
878
|
-
id: comment.id,
|
|
879
|
-
body: comment.body,
|
|
880
|
-
createdAt: comment.createdAt
|
|
881
|
-
}
|
|
882
|
-
},
|
|
883
|
-
metadata: {
|
|
884
1019
|
commentId: comment.id,
|
|
885
|
-
issueId
|
|
1020
|
+
issueId: issue.id
|
|
886
1021
|
}
|
|
887
1022
|
};
|
|
888
1023
|
} catch (error) {
|
|
889
|
-
logger6.error("Failed to create
|
|
1024
|
+
logger6.error("Failed to create comment:", error);
|
|
890
1025
|
return {
|
|
891
|
-
|
|
892
|
-
|
|
1026
|
+
text: `Failed to create comment: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1027
|
+
success: false
|
|
893
1028
|
};
|
|
894
1029
|
}
|
|
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
|
-
]
|
|
1030
|
+
}
|
|
903
1031
|
};
|
|
904
1032
|
|
|
905
1033
|
// src/actions/listTeams.ts
|
|
906
|
-
import {
|
|
907
|
-
|
|
908
|
-
} from "@elizaos/core";
|
|
909
|
-
var listLinearTeamsAction = {
|
|
1034
|
+
import { logger as logger7 } from "@elizaos/core";
|
|
1035
|
+
var listTeamsAction = {
|
|
910
1036
|
name: "LIST_LINEAR_TEAMS",
|
|
911
|
-
description: "List all teams in
|
|
912
|
-
similes: ["
|
|
913
|
-
|
|
1037
|
+
description: "List all teams in Linear",
|
|
1038
|
+
similes: ["list-linear-teams", "show-linear-teams", "get-linear-teams"],
|
|
1039
|
+
examples: [[
|
|
1040
|
+
{
|
|
1041
|
+
name: "User",
|
|
1042
|
+
content: {
|
|
1043
|
+
text: "Show me all teams"
|
|
1044
|
+
}
|
|
1045
|
+
},
|
|
1046
|
+
{
|
|
1047
|
+
name: "Assistant",
|
|
1048
|
+
content: {
|
|
1049
|
+
text: "I'll list all the teams in Linear for you.",
|
|
1050
|
+
actions: ["LIST_LINEAR_TEAMS"]
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
], [
|
|
1054
|
+
{
|
|
1055
|
+
name: "User",
|
|
1056
|
+
content: {
|
|
1057
|
+
text: "What teams are available?"
|
|
1058
|
+
}
|
|
1059
|
+
},
|
|
1060
|
+
{
|
|
1061
|
+
name: "Assistant",
|
|
1062
|
+
content: {
|
|
1063
|
+
text: "Let me show you all the available teams.",
|
|
1064
|
+
actions: ["LIST_LINEAR_TEAMS"]
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
]],
|
|
1068
|
+
async validate(runtime, _message, _state) {
|
|
914
1069
|
try {
|
|
915
|
-
const
|
|
916
|
-
return !!
|
|
1070
|
+
const apiKey = runtime.getSetting("LINEAR_API_KEY");
|
|
1071
|
+
return !!apiKey;
|
|
917
1072
|
} catch {
|
|
918
1073
|
return false;
|
|
919
1074
|
}
|
|
920
1075
|
},
|
|
921
|
-
async handler(runtime,
|
|
1076
|
+
async handler(runtime, _message, _state, _options) {
|
|
922
1077
|
try {
|
|
923
1078
|
const linearService = runtime.getService("linear");
|
|
924
1079
|
if (!linearService) {
|
|
925
1080
|
throw new Error("Linear service not available");
|
|
926
1081
|
}
|
|
927
1082
|
const teams = await linearService.getTeams();
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
1083
|
+
if (teams.length === 0) {
|
|
1084
|
+
return {
|
|
1085
|
+
text: "No teams found in Linear.",
|
|
1086
|
+
success: true,
|
|
1087
|
+
data: {
|
|
1088
|
+
teams: []
|
|
1089
|
+
}
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
const teamList = teams.map(
|
|
1093
|
+
(team, index) => `${index + 1}. ${team.name} (${team.key})${team.description ? ` - ${team.description}` : ""}`
|
|
1094
|
+
).join("\n");
|
|
935
1095
|
return {
|
|
1096
|
+
text: `Found ${teams.length} team${teams.length === 1 ? "" : "s"}:
|
|
1097
|
+
${teamList}`,
|
|
936
1098
|
success: true,
|
|
937
1099
|
data: {
|
|
938
|
-
teams:
|
|
1100
|
+
teams: teams.map((t) => ({
|
|
1101
|
+
id: t.id,
|
|
1102
|
+
name: t.name,
|
|
1103
|
+
key: t.key,
|
|
1104
|
+
description: t.description
|
|
1105
|
+
})),
|
|
939
1106
|
count: teams.length
|
|
940
|
-
},
|
|
941
|
-
metadata: {
|
|
942
|
-
teamCount: teams.length
|
|
943
1107
|
}
|
|
944
1108
|
};
|
|
945
1109
|
} catch (error) {
|
|
946
|
-
logger7.error("Failed to list
|
|
1110
|
+
logger7.error("Failed to list teams:", error);
|
|
947
1111
|
return {
|
|
948
|
-
|
|
949
|
-
|
|
1112
|
+
text: `Failed to list teams: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1113
|
+
success: false
|
|
950
1114
|
};
|
|
951
1115
|
}
|
|
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
|
-
]
|
|
1116
|
+
}
|
|
960
1117
|
};
|
|
961
1118
|
|
|
962
1119
|
// src/actions/listProjects.ts
|
|
963
|
-
import {
|
|
964
|
-
|
|
965
|
-
} from "@elizaos/core";
|
|
966
|
-
var listLinearProjectsAction = {
|
|
1120
|
+
import { logger as logger8 } from "@elizaos/core";
|
|
1121
|
+
var listProjectsAction = {
|
|
967
1122
|
name: "LIST_LINEAR_PROJECTS",
|
|
968
|
-
description: "List projects in Linear
|
|
969
|
-
similes: ["
|
|
970
|
-
|
|
1123
|
+
description: "List all projects in Linear",
|
|
1124
|
+
similes: ["list-linear-projects", "show-linear-projects", "get-linear-projects"],
|
|
1125
|
+
examples: [[
|
|
1126
|
+
{
|
|
1127
|
+
name: "User",
|
|
1128
|
+
content: {
|
|
1129
|
+
text: "Show me all projects"
|
|
1130
|
+
}
|
|
1131
|
+
},
|
|
1132
|
+
{
|
|
1133
|
+
name: "Assistant",
|
|
1134
|
+
content: {
|
|
1135
|
+
text: "I'll list all the projects in Linear for you.",
|
|
1136
|
+
actions: ["LIST_LINEAR_PROJECTS"]
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
], [
|
|
1140
|
+
{
|
|
1141
|
+
name: "User",
|
|
1142
|
+
content: {
|
|
1143
|
+
text: "What projects do we have?"
|
|
1144
|
+
}
|
|
1145
|
+
},
|
|
1146
|
+
{
|
|
1147
|
+
name: "Assistant",
|
|
1148
|
+
content: {
|
|
1149
|
+
text: "Let me show you all the available projects.",
|
|
1150
|
+
actions: ["LIST_LINEAR_PROJECTS"]
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
]],
|
|
1154
|
+
async validate(runtime, _message, _state) {
|
|
971
1155
|
try {
|
|
972
|
-
const
|
|
973
|
-
return !!
|
|
1156
|
+
const apiKey = runtime.getSetting("LINEAR_API_KEY");
|
|
1157
|
+
return !!apiKey;
|
|
974
1158
|
} catch {
|
|
975
1159
|
return false;
|
|
976
1160
|
}
|
|
977
1161
|
},
|
|
978
|
-
async handler(runtime,
|
|
1162
|
+
async handler(runtime, _message, _state, _options) {
|
|
979
1163
|
try {
|
|
980
1164
|
const linearService = runtime.getService("linear");
|
|
981
1165
|
if (!linearService) {
|
|
982
1166
|
throw new Error("Linear service not available");
|
|
983
1167
|
}
|
|
984
|
-
const
|
|
985
|
-
|
|
986
|
-
|
|
1168
|
+
const projects = await linearService.getProjects();
|
|
1169
|
+
if (projects.length === 0) {
|
|
1170
|
+
return {
|
|
1171
|
+
text: "No projects found in Linear.",
|
|
1172
|
+
success: true,
|
|
1173
|
+
data: {
|
|
1174
|
+
projects: []
|
|
1175
|
+
}
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
const projectsWithDetails = await Promise.all(
|
|
987
1179
|
projects.map(async (project) => {
|
|
988
|
-
const
|
|
1180
|
+
const teamsQuery = await project.teams();
|
|
1181
|
+
const teams = await teamsQuery.nodes;
|
|
989
1182
|
return {
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
description: project.description,
|
|
993
|
-
state: project.state,
|
|
994
|
-
teamName: team?.name,
|
|
995
|
-
startDate: project.startDate,
|
|
996
|
-
targetDate: project.targetDate
|
|
1183
|
+
...project,
|
|
1184
|
+
teamsList: teams
|
|
997
1185
|
};
|
|
998
1186
|
})
|
|
999
1187
|
);
|
|
1000
|
-
|
|
1188
|
+
const projectList = projectsWithDetails.map((project, index) => {
|
|
1189
|
+
const teamNames = project.teamsList.map((t) => t.name).join(", ") || "No teams";
|
|
1190
|
+
return `${index + 1}. ${project.name}${project.description ? ` - ${project.description}` : ""} (Teams: ${teamNames})`;
|
|
1191
|
+
}).join("\n");
|
|
1001
1192
|
return {
|
|
1193
|
+
text: `Found ${projects.length} project${projects.length === 1 ? "" : "s"}:
|
|
1194
|
+
${projectList}`,
|
|
1002
1195
|
success: true,
|
|
1003
1196
|
data: {
|
|
1004
|
-
projects:
|
|
1197
|
+
projects: projectsWithDetails.map((p) => ({
|
|
1198
|
+
id: p.id,
|
|
1199
|
+
name: p.name,
|
|
1200
|
+
description: p.description,
|
|
1201
|
+
url: p.url,
|
|
1202
|
+
teams: p.teamsList.map((t) => ({
|
|
1203
|
+
id: t.id,
|
|
1204
|
+
name: t.name,
|
|
1205
|
+
key: t.key
|
|
1206
|
+
})),
|
|
1207
|
+
state: p.state,
|
|
1208
|
+
progress: p.progress,
|
|
1209
|
+
startDate: p.startDate,
|
|
1210
|
+
targetDate: p.targetDate
|
|
1211
|
+
})),
|
|
1005
1212
|
count: projects.length
|
|
1006
|
-
},
|
|
1007
|
-
metadata: {
|
|
1008
|
-
projectCount: projects.length,
|
|
1009
|
-
teamFilter: teamId
|
|
1010
1213
|
}
|
|
1011
1214
|
};
|
|
1012
1215
|
} catch (error) {
|
|
1013
|
-
logger8.error("Failed to list
|
|
1216
|
+
logger8.error("Failed to list projects:", error);
|
|
1014
1217
|
return {
|
|
1015
|
-
|
|
1016
|
-
|
|
1218
|
+
text: `Failed to list projects: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1219
|
+
success: false
|
|
1017
1220
|
};
|
|
1018
1221
|
}
|
|
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
|
-
]
|
|
1222
|
+
}
|
|
1027
1223
|
};
|
|
1028
1224
|
|
|
1029
1225
|
// src/actions/getActivity.ts
|
|
1030
|
-
import {
|
|
1031
|
-
|
|
1032
|
-
} from "@elizaos/core";
|
|
1033
|
-
var getLinearActivityAction = {
|
|
1226
|
+
import { logger as logger9 } from "@elizaos/core";
|
|
1227
|
+
var getActivityAction = {
|
|
1034
1228
|
name: "GET_LINEAR_ACTIVITY",
|
|
1035
|
-
description: "Get recent Linear activity
|
|
1036
|
-
similes: ["
|
|
1037
|
-
|
|
1229
|
+
description: "Get recent Linear activity",
|
|
1230
|
+
similes: ["get-linear-activity", "show-linear-activity", "view-linear-activity"],
|
|
1231
|
+
examples: [[
|
|
1232
|
+
{
|
|
1233
|
+
name: "User",
|
|
1234
|
+
content: {
|
|
1235
|
+
text: "Show me recent Linear activity"
|
|
1236
|
+
}
|
|
1237
|
+
},
|
|
1238
|
+
{
|
|
1239
|
+
name: "Assistant",
|
|
1240
|
+
content: {
|
|
1241
|
+
text: "I'll check the recent Linear activity for you.",
|
|
1242
|
+
actions: ["GET_LINEAR_ACTIVITY"]
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
], [
|
|
1246
|
+
{
|
|
1247
|
+
name: "User",
|
|
1248
|
+
content: {
|
|
1249
|
+
text: "What's been happening in Linear?"
|
|
1250
|
+
}
|
|
1251
|
+
},
|
|
1252
|
+
{
|
|
1253
|
+
name: "Assistant",
|
|
1254
|
+
content: {
|
|
1255
|
+
text: "Let me show you the recent Linear activity.",
|
|
1256
|
+
actions: ["GET_LINEAR_ACTIVITY"]
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
]],
|
|
1260
|
+
async validate(runtime, _message, _state) {
|
|
1038
1261
|
try {
|
|
1039
|
-
const
|
|
1040
|
-
return !!
|
|
1262
|
+
const apiKey = runtime.getSetting("LINEAR_API_KEY");
|
|
1263
|
+
return !!apiKey;
|
|
1041
1264
|
} catch {
|
|
1042
1265
|
return false;
|
|
1043
1266
|
}
|
|
1044
1267
|
},
|
|
1045
|
-
async handler(runtime,
|
|
1268
|
+
async handler(runtime, _message, _state, _options) {
|
|
1046
1269
|
try {
|
|
1047
1270
|
const linearService = runtime.getService("linear");
|
|
1048
1271
|
if (!linearService) {
|
|
1049
1272
|
throw new Error("Linear service not available");
|
|
1050
1273
|
}
|
|
1051
|
-
const
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1274
|
+
const activity = linearService.getActivityLog();
|
|
1275
|
+
if (activity.length === 0) {
|
|
1276
|
+
return {
|
|
1277
|
+
text: "No recent Linear activity found.",
|
|
1278
|
+
success: true,
|
|
1279
|
+
data: {
|
|
1280
|
+
activity: []
|
|
1281
|
+
}
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
const activityText = activity.slice(0, 10).map((item, index) => {
|
|
1285
|
+
const description = `${item.action} ${item.resource_type} ${item.resource_id}${item.error ? ` (failed: ${item.error})` : ""}`;
|
|
1286
|
+
return `${index + 1}. ${description}`;
|
|
1287
|
+
}).join("\n");
|
|
1055
1288
|
return {
|
|
1289
|
+
text: `Recent Linear activity:
|
|
1290
|
+
${activityText}`,
|
|
1056
1291
|
success: true,
|
|
1057
1292
|
data: {
|
|
1058
|
-
activity,
|
|
1293
|
+
activity: activity.slice(0, 10),
|
|
1059
1294
|
count: activity.length
|
|
1060
|
-
},
|
|
1061
|
-
metadata: {
|
|
1062
|
-
activityCount: activity.length
|
|
1063
1295
|
}
|
|
1064
1296
|
};
|
|
1065
1297
|
} catch (error) {
|
|
1066
1298
|
logger9.error("Failed to get Linear activity:", error);
|
|
1067
1299
|
return {
|
|
1068
|
-
|
|
1069
|
-
|
|
1300
|
+
text: `Failed to get Linear activity: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1301
|
+
success: false
|
|
1070
1302
|
};
|
|
1071
1303
|
}
|
|
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
|
-
]
|
|
1304
|
+
}
|
|
1080
1305
|
};
|
|
1081
1306
|
|
|
1082
1307
|
// src/actions/clearActivity.ts
|
|
1083
|
-
import {
|
|
1084
|
-
|
|
1085
|
-
} from "@elizaos/core";
|
|
1086
|
-
var clearLinearActivityAction = {
|
|
1308
|
+
import { logger as logger10 } from "@elizaos/core";
|
|
1309
|
+
var clearActivityAction = {
|
|
1087
1310
|
name: "CLEAR_LINEAR_ACTIVITY",
|
|
1088
1311
|
description: "Clear the Linear activity log",
|
|
1089
|
-
similes: ["clear
|
|
1090
|
-
|
|
1312
|
+
similes: ["clear-linear-activity", "reset-linear-activity", "delete-linear-activity"],
|
|
1313
|
+
examples: [[
|
|
1314
|
+
{
|
|
1315
|
+
name: "User",
|
|
1316
|
+
content: {
|
|
1317
|
+
text: "Clear the Linear activity log"
|
|
1318
|
+
}
|
|
1319
|
+
},
|
|
1320
|
+
{
|
|
1321
|
+
name: "Assistant",
|
|
1322
|
+
content: {
|
|
1323
|
+
text: "I'll clear the Linear activity log for you.",
|
|
1324
|
+
actions: ["CLEAR_LINEAR_ACTIVITY"]
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
], [
|
|
1328
|
+
{
|
|
1329
|
+
name: "User",
|
|
1330
|
+
content: {
|
|
1331
|
+
text: "Reset Linear activity"
|
|
1332
|
+
}
|
|
1333
|
+
},
|
|
1334
|
+
{
|
|
1335
|
+
name: "Assistant",
|
|
1336
|
+
content: {
|
|
1337
|
+
text: "I'll reset the Linear activity log now.",
|
|
1338
|
+
actions: ["CLEAR_LINEAR_ACTIVITY"]
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
]],
|
|
1342
|
+
async validate(runtime, _message, _state) {
|
|
1091
1343
|
try {
|
|
1092
|
-
const
|
|
1093
|
-
return !!
|
|
1344
|
+
const apiKey = runtime.getSetting("LINEAR_API_KEY");
|
|
1345
|
+
return !!apiKey;
|
|
1094
1346
|
} catch {
|
|
1095
1347
|
return false;
|
|
1096
1348
|
}
|
|
1097
1349
|
},
|
|
1098
|
-
async handler(runtime, message,
|
|
1350
|
+
async handler(runtime, message, _state, _options) {
|
|
1099
1351
|
try {
|
|
1100
1352
|
const linearService = runtime.getService("linear");
|
|
1101
1353
|
if (!linearService) {
|
|
1102
1354
|
throw new Error("Linear service not available");
|
|
1103
1355
|
}
|
|
1104
|
-
linearService.clearActivityLog();
|
|
1105
|
-
logger10.info("Cleared Linear activity log");
|
|
1356
|
+
await linearService.clearActivityLog();
|
|
1106
1357
|
return {
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
message: "Activity log cleared successfully"
|
|
1110
|
-
}
|
|
1358
|
+
text: "Linear activity log has been cleared.",
|
|
1359
|
+
success: true
|
|
1111
1360
|
};
|
|
1112
1361
|
} catch (error) {
|
|
1113
1362
|
logger10.error("Failed to clear Linear activity:", error);
|
|
1114
1363
|
return {
|
|
1115
|
-
|
|
1116
|
-
|
|
1364
|
+
text: `Failed to clear Linear activity: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1365
|
+
success: false
|
|
1117
1366
|
};
|
|
1118
1367
|
}
|
|
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
|
-
]
|
|
1368
|
+
}
|
|
1127
1369
|
};
|
|
1128
1370
|
|
|
1129
1371
|
// src/providers/issues.ts
|
|
@@ -1299,29 +1541,26 @@ ${activityList.join("\n")}`;
|
|
|
1299
1541
|
|
|
1300
1542
|
// src/index.ts
|
|
1301
1543
|
var linearPlugin = {
|
|
1302
|
-
name: "linear",
|
|
1303
|
-
description: "
|
|
1544
|
+
name: "@elizaos/plugin-linear",
|
|
1545
|
+
description: "Plugin for integrating with Linear issue tracking system",
|
|
1304
1546
|
services: [LinearService],
|
|
1305
1547
|
actions: [
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1548
|
+
createIssueAction,
|
|
1549
|
+
getIssueAction,
|
|
1550
|
+
updateIssueAction,
|
|
1551
|
+
searchIssuesAction,
|
|
1552
|
+
createCommentAction,
|
|
1553
|
+
listTeamsAction,
|
|
1554
|
+
listProjectsAction,
|
|
1555
|
+
getActivityAction,
|
|
1556
|
+
clearActivityAction
|
|
1315
1557
|
],
|
|
1316
1558
|
providers: [
|
|
1317
1559
|
linearIssuesProvider,
|
|
1318
1560
|
linearTeamsProvider,
|
|
1319
1561
|
linearProjectsProvider,
|
|
1320
1562
|
linearActivityProvider
|
|
1321
|
-
]
|
|
1322
|
-
// No evaluators or events for this plugin
|
|
1323
|
-
evaluators: [],
|
|
1324
|
-
events: {}
|
|
1563
|
+
]
|
|
1325
1564
|
};
|
|
1326
1565
|
export {
|
|
1327
1566
|
LinearAPIError,
|