12 KiB
@elizaos/plugin-linear
Linear issue-tracking integration for elizaOS. Adds issue/comment/project/team CRUD, activity tracking, and a linear_issues search category to any Eliza agent that has a Linear API key configured.
Purpose / role
This plugin connects an Eliza agent to Linear via the @linear/sdk client. It registers a single composite LINEAR action (routing to 11 sub-operations via regex + explicit op dispatch), four context providers, and the LinearService singleton. It is opt-in — add the package and set LINEAR_API_KEY; the plugin validates the key at startup and refuses to enable the action if no account is configured.
Plugin surface
Action
| Name | Description |
|---|---|
LINEAR |
Single router action. Dispatches to one of 11 sub-ops (see below) based on the action parameter or regex-matched message text. Similes include all permutations like CREATE_LINEAR_ISSUE, SEARCH_LINEAR_ISSUES, etc. |
Sub-operations routed by LINEAR:
| Op | Effect |
|---|---|
create_issue |
Create a new issue in a team |
get_issue |
Fetch issue details by identifier (e.g. ENG-123) |
update_issue |
Update title, description, priority, assignee, labels, state, estimate, or due date |
delete_issue |
Archive an issue (Linear uses archive, not hard delete) |
search_issues |
Filter issues by query, state, assignee, label, project, team, priority |
create_comment |
Add a comment body to an issue |
update_comment |
Update a comment's body by comment ID |
delete_comment |
Delete a comment by ID |
list_comments |
List comments on an issue |
get_activity |
Return the in-memory activity log (last 1000 ops, configurable limit) |
clear_activity |
Wipe the activity log, optionally scoped to an account |
Providers
| Name | Context gate | Cache | Description |
|---|---|---|---|
LINEAR_ISSUES |
automation, connectors |
turn |
Up to 10 recent issues with state + assignee |
LINEAR_TEAMS |
automation, connectors |
agent |
Up to 20 teams with key, name, description |
LINEAR_PROJECTS |
automation, connectors |
agent |
Up to 10 active (started/planned) projects |
LINEAR_ACTIVITY |
automation, connectors |
turn |
Last 10 activity log entries |
All four require minRole: ADMIN.
Service
| Name | serviceType |
Description |
|---|---|---|
LinearService |
"linear" |
Wraps @linear/sdk LinearClient; manages multi-account map; exposes typed methods for all CRUD ops; maintains an in-memory activity log (capped at 1000 entries). |
Search category
linear_issues — registered via registerLinearSearchCategory on init. Supports filters: query, state, assignee, label, project, team, priority, limit, accountId.
Connector account provider
createLinearConnectorAccountProvider registers this plugin with the core ConnectorAccountManager, enabling OAuth and API-key account lifecycle (list, create, patch, delete, startOAuth, completeOAuth).
Layout
src/
index.ts Plugin entry — exports linearPlugin, LinearService, accounts utilities
accounts.ts readLinearAccounts(), resolveLinearDefaultAccount(), hasLinearAccountConfig()
connector-account-provider.ts ConnectorAccountProvider implementation (OAuth flow + account CRUD)
search-category.ts registerLinearSearchCategory() + LINEAR_ISSUES_SEARCH_CATEGORY definition
prompts.ts LLM prompt templates used by action handlers
types/
index.ts LinearIssueInput, LinearCommentInput, LinearSearchFilters, error classes
services/
linear.ts LinearService — main SDK wrapper, multi-account client map, activity log
providers/
issues.ts LINEAR_ISSUES provider
teams.ts LINEAR_TEAMS provider
projects.ts LINEAR_PROJECTS provider
activity.ts LINEAR_ACTIVITY provider
actions/
linear.ts linearAction router — ROUTES table, selectRoute(), dispatch logic
createIssue.ts create_issue sub-action handler
getIssue.ts get_issue sub-action handler
updateIssue.ts update_issue sub-action handler (handleUpdateIssue)
deleteIssue.ts delete_issue sub-action handler
searchIssues.ts search_issues sub-action handler
createComment.ts create_comment sub-action handler
updateComment.ts update_comment sub-action handler (handleUpdateComment)
deleteComment.ts delete_comment sub-action handler
listComments.ts list_comments sub-action handler
getActivity.ts get_activity sub-action handler
clearActivity.ts clear_activity sub-action handler
account-options.ts linearAccountIdParameter — shared action parameter definition
routers.ts Routing helpers
parseLinearPrompt.ts LLM-assisted prompt parsing for complex inputs
validate-linear-intent.ts Intent validation utilities
index.ts Re-exports
Commands
bun run --cwd plugins/plugin-linear build # Bun.build bundle + tsc .d.ts → dist/
bun run --cwd plugins/plugin-linear dev # bun --hot build.ts (rebuild on change)
bun run --cwd plugins/plugin-linear test # vitest run
bun run --cwd plugins/plugin-linear test:watch # vitest watch
bun run --cwd plugins/plugin-linear typecheck # tsgo --noEmit
bun run --cwd plugins/plugin-linear lint # biome check --write --unsafe
bun run --cwd plugins/plugin-linear format # biome format --write
bun run --cwd plugins/plugin-linear clean # rm dist .turbo artifacts
Config / env vars
| Variable | Required | Description |
|---|---|---|
LINEAR_API_KEY |
Yes | Personal or workspace API key from linear.app/settings/api |
LINEAR_WORKSPACE_ID |
No | Workspace ID; used as the default workspace context |
LINEAR_DEFAULT_TEAM_KEY |
No | Team key (e.g. ENG) — default team for issue creation when not specified |
LINEAR_ACCOUNT_ID |
No | Override the account ID for the single-API-key account (default: "default") |
LINEAR_DEFAULT_ACCOUNT_ID |
No | Alias for LINEAR_ACCOUNT_ID |
LINEAR_ACCOUNTS |
No | JSON array/object of multi-account configs — each entry accepts apiKey, accountId, workspaceId, defaultTeamKey, label |
LINEAR_OAUTH_CLIENT_ID |
OAuth only | Linear OAuth app client ID |
LINEAR_OAUTH_CLIENT_SECRET |
OAuth only | Linear OAuth app client secret |
LINEAR_OAUTH_REDIRECT_URI |
OAuth only | OAuth redirect URI registered with Linear |
Multi-account config via LINEAR_ACCOUNTS accepts either a JSON array or object keyed by account ID. Within each record the plugin also reads nested credentials, metadata, and settings sub-objects for apiKey / workspaceId / defaultTeamKey.
Character-file override: character.settings.linear.accounts (array or object) is read before env vars.
How to extend
Add a new sub-operation
- Create
src/actions/<opName>.tsexporting anActionor aLinearHandlerFn. - Add the op string to the
LinearOpunion andALL_OPSarray insrc/actions/linear.ts. - Add a
LinearRouteentry toROUTESwithop, amatchregex, and eitheractionorrun. - Import and wire the handler in the ROUTES table.
- Expose the new simile strings in
linearAction.similesif needed.
Add a new provider
- Create
src/providers/<name>.tsexporting aProviderobject withname,get,contexts,contextGate. - Import and add it to the
providersarray insrc/index.ts.
Add a new service method
- Add the method to
LinearServiceinsrc/services/linear.ts. - Call
this.logActivity(...)at the end so the activity log stays consistent. - If a new input type is needed, add it to
src/types/index.ts.
Conventions / gotchas
- The plugin name in
package.jsonis@elizaos/plugin-linearbut the exportedPlugin.nameis"@elizaos/plugin-linear-ts". Keep both as-is — they serve different registry purposes. deleteIssuecallsclient.archiveIssue— Linear does not support true hard-deletes on issues via the public API.- Activity log is in-memory and resets on service stop. Max 1000 entries (oldest evicted).
- Multi-account lookups fall through: explicit
accountId→defaultAccountIdsetting → first registered account. - Providers are gated to
minRole: ADMINandcontexts: ["automation", "connectors"]. They will not appear in general conversation context. - The
LINEARaction usespromoteSubactionsToActionsso sub-action names are also available as top-level action names in the agent runtime. - OAuth flow requires
LINEAR_OAUTH_CLIENT_ID,LINEAR_OAUTH_CLIENT_SECRET, andLINEAR_OAUTH_REDIRECT_URI. API-key flow does not need any of these. - See the root
AGENTS.mdfor repo-wide conventions (logger-only, ESM, naming, architecture rules).
⛔ NON-NEGOTIABLE — evidence, trajectories & real end-to-end tests
The binding, repo-wide standard is AGENTS.md. Read it. Nothing in this package is done until it is proven done — a reviewer must confirm it works without reading the code, from the artifacts you attach. This applies to every feature, fix, refactor, and chore here. "Tests pass" is not proof; "CI is green" is not proof.
- Record AND read model trajectories. Capture the actual inputs and outputs of the model
from a live LLM — not the deterministic proxy, not a mock: the prompt, the
providers/context, the raw model output, every tool/action call, and the result. Then open
the trajectory and review it by hand. A captured-but-unread trajectory is not evidence
(
packages/scenario-runner/bin/eliza-scenarios run <scenario> --report <out>). - Real, full-featured E2E — no larp. Every feature ships detailed end-to-end tests that drive the real path end to end. Not the happy "front door" only: cover error paths, edge/empty/invalid input, concurrency, roles/permissions, and adversarial input. A test that asserts against a mock/stub/fixture standing in for the thing under test does not count. If the real model/device/chain/connector/account is hard to reach, make it reachable — that is the work, not an excuse to mock. If the existing tests here are shallow or mocked, fixing them is part of your change.
- Screenshots + logs at every phase, plus a complete walkthrough video/run-through of
the entire feature or view, start to finish (
bun run test:e2e:record). - Manually review every artifact the change touches — never just the green check: client
logs (console + network), server logs (
[ClassName] …), the model trajectories in and out, before/after full-page screenshots, and the domain artifacts listed below for this package. - No residuals. No shortcuts. The goal is not "done" — it is everything done. Clear every blocker by the hard path: build the real architecture, stand up the real model/device/service, actually test it. Never leave a TODO, a stub, a stepping-stone, or a "follow-up." When unsure, research thoroughly, weigh the options, and ship the best, highest-effort, production-ready version. Keep going until every possibility is exhausted.
Artifacts → attached inline in the PR (MP4 video, JPG screenshots, logs in <details>); attach each evidence type or
explicitly mark it N/A with a reason — never leave it blank. If develop moved and changed
behavior, re-capture evidence; stale proof is worse than none.
Capture & manually review for this package — platform connector:
- A real (or sandbox-account) round-trip on the platform: inbound message → agent → outbound reply, captured as logs and a screenshot/recording of the actual conversation.
- The raw inbound event/webhook payload and the outbound API request/response, with IDs mapped correctly (
stringToUuid/createUniqueUuid). - Attachments, threads/replies, edits, multi-account, and rate-limit/error paths — not just a single text ping.
- The agent trajectory for the turn the connector drove.