{"id":44663941,"url":"https://github.com/stepandel/openclaw-linear","last_synced_at":"2026-02-21T22:16:33.453Z","repository":{"id":338148486,"uuid":"1156739130","full_name":"stepandel/openclaw-linear","owner":"stepandel","description":"Linear plugin for OpenClaw","archived":false,"fork":false,"pushed_at":"2026-02-16T23:48:34.000Z","size":330,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-17T05:47:02.485Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stepandel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-13T01:55:04.000Z","updated_at":"2026-02-16T23:47:56.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/stepandel/openclaw-linear","commit_stats":null,"previous_names":["stepandel/openclaw-linear"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/stepandel/openclaw-linear","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stepandel%2Fopenclaw-linear","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stepandel%2Fopenclaw-linear/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stepandel%2Fopenclaw-linear/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stepandel%2Fopenclaw-linear/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stepandel","download_url":"https://codeload.github.com/stepandel/openclaw-linear/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stepandel%2Fopenclaw-linear/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29695437,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-21T18:18:25.093Z","status":"ssl_error","status_checked_at":"2026-02-21T18:18:22.435Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-02-15T00:13:25.204Z","updated_at":"2026-02-21T22:16:33.447Z","avatar_url":"https://github.com/stepandel.png","language":"TypeScript","funding_links":[],"categories":["Skills \u0026 Plugins"],"sub_categories":["Notable Skills \u0026 Plugins"],"readme":"# openclaw-linear\n\nLinear integration for [OpenClaw](https://github.com/nichochar/openclaw). Receives Linear webhook events, routes them through a persistent work queue, and gives agents tools to manage issues, comments, projects, teams, and relations via the Linear GraphQL API.\n\n## Install\n\n```bash\nopenclaw plugins install openclaw-linear\n```\n\n## Configuration\n\nEach OpenClaw instance runs one agent — configure a separate instance per agent.\n\n```yaml\nplugins:\n  linear:\n    apiKey: \"lin_api_...\"                # Linear API key (required)\n    webhookSecret: \"your-signing-secret\" # Webhook secret (required)\n    agentMapping:                        # Filter: only handle events for these Linear users\n      \"linear-user-uuid\": \"titus\"\n    teamIds: [\"ENG\", \"OPS\"]             # Optional: filter to specific teams (empty = all)\n    eventFilter: [\"Issue\", \"Comment\"]    # Optional: filter event types (empty = all)\n    debounceMs: 30000                    # Optional: batch window in ms (default: 30000)\n    stateActions:                        # Optional: map state types/names to queue actions\n      backlog: \"add\"\n      unstarted: \"add\"\n      started: \"ignore\"\n      \"In Review\": \"remove\"             # State names override type matches (case-insensitive)\n      completed: \"remove\"\n      canceled: \"remove\"\n```\n\n### Config Fields\n\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n| `apiKey` | string | **Yes** | Linear API key. Create at [linear.app/settings/account/security](https://linear.app/settings/account/security). |\n| `webhookSecret` | string | **Yes** | Shared secret for HMAC webhook signature verification. |\n| `agentMapping` | object | No | Maps Linear user UUIDs to agent IDs. Acts as a filter — events for unmapped users are ignored. Since each instance runs one agent, this typically has one entry. |\n| `teamIds` | string[] | No | Team keys to scope webhook processing. Empty = all teams. |\n| `eventFilter` | string[] | No | Event types to handle (`Issue`, `Comment`). Empty = all. |\n| `debounceMs` | integer | No | Debounce window in milliseconds. Events within this window are batched into a single dispatch. Default: `30000` (30s). |\n| `stateActions` | object | No | Maps Linear state types or names to queue actions (`\"add\"`, `\"remove\"`, `\"ignore\"`). See [State Actions](#state-actions). |\n\n## Webhook Setup\n\n1. **Make your endpoint publicly accessible.** The plugin registers at `/hooks/linear`:\n   ```bash\n   # Example with Tailscale Funnel\n   tailscale funnel --bg 3000\n   ```\n\n2. **Register the webhook in Linear:**\n   - Go to **Settings \u003e API \u003e Webhooks**\n   - Set the URL to `https://your-host/hooks/linear`\n   - Set the secret to match your `webhookSecret`\n   - Select event types: Issues, Comments\n   - Save\n\n3. **Verify:** Assign a Linear issue to a mapped user — the agent should receive a notification.\n\n## How It Works\n\n```text\n                         Linear Webhook POST\n                                │\n                                ▼\n                  ┌───────────────────────────┐\n                  │      Webhook Handler      │\n                  │  HMAC verify · dedup (10m) │\n                  └─────────────┬─────────────┘\n                                │\n                                ▼\n                  ┌───────────────────────────┐\n                  │       Event Router        │\n                  │  team/type filter · user  │\n                  │  mapping · state actions  │\n                  └──────┬────────────┬───────┘\n                         │            │\n                     wake         notify\n                     actions      actions\n                         │            │\n                         ▼            │\n                  ┌──────────────┐    │\n                  │   Debouncer  │    │\n                  │  (30s batch) │    │\n                  └──────┬───────┘    │\n                         │            │\n                         ▼            ▼\n                  ┌───────────────────────────┐\n                  │        Work Queue         │\n                  │  JSONL · priority-sorted  │\n                  │  dedup · crash recovery   │\n                  └─────────────┬─────────────┘\n                                │\n                           added \u003e 0?\n                          yes/      \\no\n                           │         └─▶ (skip)\n                           ▼\n                  ┌───────────────────────────┐\n                  │     Agent Dispatch        │\n                  │  \"N notification(s)       │\n                  │   queued\"                 │\n                  └─────────────┬─────────────┘\n                                │\n                                ▼\n                  ┌───────────────────────────┐\n                  │          Agent            │\n                  │  peek · pop · complete    │\n                  └─────────────┬─────────────┘\n                                │\n                           on complete\n                                │\n                         items remain?\n                          yes/      \\no\n                           │         └─▶ (idle)\n                           ▼\n                      auto-wake\n                    (new session)\n```\n\nEvents flow through four stages. The **webhook handler** verifies signatures and deduplicates deliveries. The **event router** filters by team, type, and user, then classifies each event as `wake` (needs the agent's attention now) or `notify` (queue silently). Wake actions pass through a **debouncer** that batches events within a configurable window. Both paths write to the **work queue** — a persistent, priority-sorted JSONL file. The agent is only woken when new items are actually added (deduplication may suppress a dispatch). After the agent completes an item, **auto-wake** checks for remaining work and starts a fresh session if needed.\n\n## Work Queue\n\nThe work queue is the central data structure. Every webhook event that needs agent attention passes through it. No LLM tokens are spent on triage — queue writes are fully deterministic.\n\n### Storage\n\nItems are persisted to a JSONL file (`queue/inbox.jsonl` in the plugin data directory). File writes use atomic temp-file + fsync + rename to prevent corruption. A mutex serializes all operations to prevent race conditions.\n\n### Item Lifecycle\n\n```text\n  webhook event\n       │\n       ▼\n   ┌────────┐   pop/drain   ┌─────────────┐   complete   ┌─────────┐\n   │pending │ ─────────────▶ │ in_progress │ ───────────▶ │ removed │\n   └────────┘                └─────────────┘              └─────────┘\n       │                            │\n       │  removal event             │  crash recovery\n       ▼                            ▼\n   (removed)                   (→ pending)\n```\n\n1. **Enqueue** — webhook events create `pending` items, deduped by `issueId:event`\n2. **Claim** — `pop` (single) or `drain` (all) moves items to `in_progress`\n3. **Complete** — removes the `in_progress` item from the file\n4. **Crash recovery** — on startup, all `in_progress` items reset to `pending`\n\n### Priority Sorting\n\nItems sort by Linear priority (1 = urgent, 4 = low). Priority 0 (none) maps to 5 so unprioritized items sort last. Ties break by timestamp (oldest first). Priority changes from Linear update items in-place.\n\n### Deduplication\n\nEach item has a dedup key of `issueId:event` (e.g. `ENG-42:ticket`). The same issue can appear twice with different event types (one ticket + one mention). If a duplicate already exists in the queue (any status), the new entry is skipped.\n\n### Removal Events\n\nWhen an issue is unassigned, reassigned away, or moved to a `remove` state, any matching `ticket` item is removed from the queue — even if already `in_progress`. This prevents the agent from working on stale assignments.\n\n### Queue Events\n\n| Agent Event | Queue Event | Behavior |\n|---|---|---|\n| `issue.assigned` | `ticket` | Enqueue + wake |\n| `issue.state_readded` | `ticket` | Enqueue + wake |\n| `comment.mention` | `mention` | Enqueue + wake |\n| `issue.unassigned` | — | Remove ticket |\n| `issue.reassigned` | — | Remove ticket |\n| `issue.state_removed` | — | Remove ticket |\n| `issue.priority_changed` | — | Update priority in-place |\n\n### Agent Tool\n\nThe `linear_queue` tool gives agents four actions:\n\n| Action | Description |\n|--------|-------------|\n| `peek` | View all pending items sorted by priority |\n| `pop` | Claim the highest-priority pending item |\n| `drain` | Claim all pending items at once |\n| `complete` | Finish work on a claimed item (requires `issueId`) |\n\n## Tools\n\nThe plugin provides six tools. All use an `action` parameter to select the operation.\n\n### `linear_issue` — issue management\n\n| Action | Required | Optional |\n|--------|----------|----------|\n| `view` | `issueId` | — |\n| `list` | — | `state`, `assignee`, `team`, `project`, `limit` |\n| `create` | `title` | `description`, `assignee`, `state`, `priority`, `team`, `project`, `parent`, `labels` |\n| `update` | `issueId` | `title`, `description`, `assignee`, `state`, `priority`, `labels`, `project` |\n| `delete` | `issueId` | — |\n\nIssues are referenced by human-readable identifiers (e.g. `ENG-123`). Names are resolved automatically — `assignee` accepts display names or emails, `state` accepts workflow state names, `team` accepts team keys, and `labels` accepts label names.\n\n### `linear_comment` — comments\n\n| Action | Required | Optional |\n|--------|----------|----------|\n| `list` | `issueId` | — |\n| `add` | `issueId`, `body` | `parentCommentId` |\n| `update` | `commentId`, `body` | — |\n\n### `linear_team` — teams and members\n\n| Action | Required |\n|--------|----------|\n| `list` | — |\n| `members` | `team` (key, e.g. `ENG`) |\n\n### `linear_project` — projects\n\n| Action | Required | Optional |\n|--------|----------|----------|\n| `list` | — | `team`, `status` |\n| `view` | `projectId` | — |\n| `create` | `name` | `team`, `description` |\n\n### `linear_relation` — issue relations\n\n| Action | Required |\n|--------|----------|\n| `list` | `issueId` |\n| `add` | `issueId`, `type`, `relatedIssueId` |\n| `delete` | `relationId` |\n\nRelation types: `blocks`, `blocked-by`, `related`, `duplicate`.\n\n## Routed Events\n\n| Linear Event | Router Action | Agent Event |\n|---|---|---|\n| Issue assigned to mapped user | `wake` | `issue.assigned` |\n| Issue unassigned from mapped user | `notify` | `issue.unassigned` |\n| Issue reassigned away from mapped user | `notify` | `issue.reassigned` |\n| Issue state change → `add` action | `wake` | `issue.state_readded` |\n| Issue state change → `remove` action | `notify` | `issue.state_removed` |\n| @mention in comment (mapped user) | `wake` | `comment.mention` |\n\n`wake` events pass through the debouncer and dispatch to the agent. `notify` events write directly to the queue without waking.\n\n## State Actions\n\nWhen an issue's state changes, the plugin resolves what to do based on the `stateActions` config. This controls which state transitions re-add issues to the queue (e.g. bounced back from testing) vs. remove them (e.g. done/canceled) vs. are ignored (e.g. in progress).\n\n**Resolution order:** state name match → state type match → built-in default.\n\nLinear has 6 fixed state types. Custom state names (e.g. \"In Review\", \"QA\") are team-specific but always belong to one of these types.\n\n**Built-in defaults** (used when `stateActions` is not configured or a state isn't mapped):\n\n| State Type | Default Action |\n|---|---|\n| `triage` | `ignore` |\n| `backlog` | `add` |\n| `unstarted` | `add` |\n| `started` | `ignore` |\n| `completed` | `remove` |\n| `canceled` | `remove` |\n\n**Actions:**\n\n- `\"add\"` — re-add the issue to the queue as a ticket and wake the agent\n- `\"remove\"` — remove the issue's ticket from the queue\n- `\"ignore\"` — do nothing (default for unmapped states)\n\n## Architecture\n\n```text\nsrc/\n├── index.ts                 # Plugin entry point, activation, dispatch logic\n├── webhook-handler.ts       # HMAC verification, body parsing, dedup\n├── event-router.ts          # Event filtering, routing, state action resolution\n├── linear-api.ts            # GraphQL client, name/ID resolution helpers\n├── work-queue.ts            # Persistent JSONL queue with priority sorting\n└── tools/\n    ├── queue-tool.ts        # linear_queue — notification inbox management\n    ├── linear-issue-tool.ts # linear_issue — CRUD for issues\n    ├── linear-comment-tool.ts # linear_comment — issue comments\n    ├── linear-team-tool.ts  # linear_team — teams and members\n    ├── linear-project-tool.ts # linear_project — project management\n    └── linear-relation-tool.ts # linear_relation — issue relations\n```\n\n## Development\n\n```bash\nnpm install\nnpm run build\nnpm test\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstepandel%2Fopenclaw-linear","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstepandel%2Fopenclaw-linear","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstepandel%2Fopenclaw-linear/lists"}