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