@calltelemetry/openclaw-linear 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,120 +1,287 @@
1
- # Linear Agent Plugin for OpenClaw
1
+ # @calltelemetry/openclaw-linear
2
2
 
3
- Webhook-driven Linear integration with OAuth support, multi-agent routing, and a 3-stage AI pipeline for issue triage and implementation.
3
+ An OpenClaw plugin that connects your Linear workspace to AI agents. Issues get triaged automatically, agents respond to @mentions, and a full plan-implement-audit pipeline runs when you assign work to the agent.
4
4
 
5
- ## What It Does
5
+ ## Features
6
6
 
7
- - **Issue triage** — When an issue is assigned/delegated to the app user, an agent estimates story points, applies labels, and posts an assessment
8
- - **Agent sessions** — Full plan-approve-implement-audit pipeline triggered from Linear's agent UI
9
- - **@mention routing** — Comment mentions like `@qa` or `@infra` route to specific role-based agents with different expertise
10
- - **App notifications** — Responds to Linear app mentions and assignments via branded comments
11
- - **Activity tracking** — Emits thought/action/response events visible in Linear's agent session UI
7
+ - **Auto-triage** — New issues get story point estimates, labels, and priority automatically
8
+ - **@mention routing** — `@qa`, `@infra`, `@docs` in comments route to specialized agents
9
+ - **Agent pipeline** — Assign an issue to the agent and it plans, implements, and audits the work
10
+ - **Branded replies** — Each agent posts with its own name and avatar in Linear
11
+ - **Real-time progress** — Agent activity (thinking, acting, responding) shows in Linear's UI
12
+ - **Unified `code_run` tool** — One tool, three coding CLI backends (Codex, Claude Code, Gemini CLI), configurable per agent
13
+ - **Issue management via `linearis`** — Agents use the `linearis` CLI to update status, close issues, add comments, and more
12
14
 
13
- ## Quick Install
15
+ ## Architecture
16
+
17
+ ### Webhook Flow
18
+
19
+ ```
20
+ Linear OpenClaw Gateway AI Agents
21
+ | | |
22
+ | Webhook (issue created) | |
23
+ | ────────────────────────>| |
24
+ | | Dispatch triage agent |
25
+ | | ───────────────────────>|
26
+ | | |
27
+ | | Estimate + labels |
28
+ | | <───────────────────────|
29
+ | Update issue | |
30
+ | <────────────────────────| |
31
+ | Post assessment comment | |
32
+ | <────────────────────────| |
33
+ ```
34
+
35
+ ```
36
+ Linear OpenClaw Gateway AI Agents
37
+ | | |
38
+ | "@qa check this" | |
39
+ | ────────────────────────>| |
40
+ | | Route to QA agent |
41
+ | | ───────────────────────>|
42
+ | | |
43
+ | | Response |
44
+ | | <───────────────────────|
45
+ | Comment from "QA" | |
46
+ | <────────────────────────| |
47
+ ```
48
+
49
+ ### Two Webhook Systems
50
+
51
+ Linear delivers events through two separate webhook paths:
52
+
53
+ 1. **Workspace webhook** (Settings > API > Webhooks) — handles Comment, Issue, and User events
54
+ 2. **OAuth app webhook** (Settings > API > Applications > your app) — handles `AgentSessionEvent` (created/prompted)
55
+
56
+ Both must point to the same URL: `https://<your-domain>/linear/webhook`
57
+
58
+ ### Source Layout
59
+
60
+ ```
61
+ index.ts Plugin entry point, CLI checks, tool/webhook registration
62
+ src/
63
+ webhook.ts Webhook handler — routes events to agents, builds prompts
64
+ pipeline.ts 3-stage pipeline: plan → implement → audit
65
+ agent.ts Agent execution wrapper
66
+ active-session.ts In-process session registry (issueId → session)
67
+
68
+ code-tool.ts Unified code_run tool — dispatches to configured backend
69
+ cli-shared.ts Shared helpers for CLI tools (buildLinearApi, resolveSession)
70
+ codex-tool.ts Codex CLI runner (JSONL stream → Linear activities)
71
+ claude-tool.ts Claude Code CLI runner (JSONL stream → Linear activities)
72
+ gemini-tool.ts Gemini CLI runner (JSONL stream → Linear activities)
73
+ coding-tools.json Backend config (default tool, per-agent overrides, aliases)
74
+
75
+ tools.ts Tool registration (code_run + orchestration)
76
+ orchestration-tools.ts spawn_agent / ask_agent for multi-agent delegation
77
+ linear-api.ts Linear GraphQL API client, token resolution, activity streaming
78
+ client.ts Lightweight Linear GraphQL client (legacy, unused by tools)
79
+ auth.ts OAuth token management and profile storage
80
+ oauth-callback.ts OAuth callback handler
81
+ cli.ts CLI subcommands (auth, status)
82
+ codex-worktree.ts Git worktree management for isolated Codex runs
83
+ ```
84
+
85
+ ## Coding Tool (`code_run`)
86
+
87
+ The plugin provides a single `code_run` tool that dispatches to one of three coding CLI backends. Agents call `code_run` without needing to know which backend is active — the dispatcher handles routing.
88
+
89
+ ### Supported Backends
90
+
91
+ | Backend | CLI | Stream Format | Key Flags |
92
+ |---|---|---|---|
93
+ | **Codex** (OpenAI) | `codex` | JSONL | `--full-auto`, `-q` |
94
+ | **Claude Code** (Anthropic) | `claude` | JSONL (`stream-json`) | `--print`, `--dangerously-skip-permissions`, `--verbose` |
95
+ | **Gemini CLI** (Google) | `gemini` | JSONL (`stream-json`) | `--yolo`, `-o stream-json` |
96
+
97
+ All three stream JSONL events that get mapped to Linear agent activities in real-time (thoughts, actions, tool results).
98
+
99
+ ### Backend Resolution Priority
100
+
101
+ When `code_run` is called:
102
+
103
+ 1. **Explicit `backend` parameter** — Agent passes `backend: "gemini"` (or any alias)
104
+ 2. **Per-agent override** — `agentCodingTools` in `coding-tools.json`
105
+ 3. **Global default** — `codingTool` in `coding-tools.json`
106
+ 4. **Hardcoded fallback** — `"claude"`
107
+
108
+ ### Configuration (`coding-tools.json`)
109
+
110
+ ```json
111
+ {
112
+ "codingTool": "codex",
113
+ "agentCodingTools": {
114
+ "kaylee": "claude",
115
+ "inara": "gemini"
116
+ },
117
+ "backends": {
118
+ "claude": {
119
+ "aliases": ["claude", "claude code", "anthropic"]
120
+ },
121
+ "codex": {
122
+ "aliases": ["codex", "openai"]
123
+ },
124
+ "gemini": {
125
+ "aliases": ["gemini", "google"]
126
+ }
127
+ }
128
+ }
129
+ ```
130
+
131
+ - **`codingTool`** — Default backend for all agents
132
+ - **`agentCodingTools`** — Per-agent overrides (keyed by agent ID)
133
+ - **`backends.*.aliases`** — Alias strings so the agent (or user) can say "use google" and it resolves to `gemini`
134
+
135
+ ### Backend-Specific Notes
136
+
137
+ **Claude Code:**
138
+ - Must unset `CLAUDECODE` env var to avoid "nested session" error
139
+ - Requires `--verbose` alongside `stream-json` for full event output
140
+ - Content blocks are arrays: `message.content[].type` can be `text` or `tool_use`
141
+
142
+ **Gemini CLI:**
143
+ - Working directory set via `spawn()` `cwd` option (no `-C` flag)
144
+ - Model override via `-m <model>` flag
145
+ - Stderr may include "YOLO mode" warnings — filtered from output
146
+
147
+ **Codex:**
148
+ - Uses git worktrees for isolated runs (see `codex-worktree.ts`)
149
+ - Model/timeout configurable via plugin config (`codexModel`, `codexTimeoutMs`)
150
+
151
+ ## Linear Issue Management (`linearis` Skill)
152
+
153
+ Issue management (update status, close, assign, comment, labels, etc.) is handled by the **`linearis`** CLI, installed as an OpenClaw skill. This replaces custom GraphQL tools — agents use `linearis` via exec.
154
+
155
+ ### Install
14
156
 
15
157
  ```bash
16
- openclaw plugins install @calltelemetry/openclaw-linear
17
- openclaw gateway restart
158
+ npx clawhub install linearis
159
+ npm install -g linearis
18
160
  ```
19
161
 
20
- That's it — the plugin is installed and enabled. Continue with the [setup steps](#setup) below to configure Linear OAuth and webhooks.
162
+ ### Auth
163
+
164
+ ```bash
165
+ echo "lin_api_..." > ~/.linear_api_token
166
+ ```
21
167
 
22
- > To install from a local checkout instead: `openclaw plugins install --link /path/to/linear`
168
+ Or set `LINEAR_API_TOKEN` env var.
169
+
170
+ ### Key Commands
171
+
172
+ ```bash
173
+ linearis issues list -l 20 # List recent issues
174
+ linearis issues list --team UAT # Filter by team
175
+ linearis issues search "auth bug" # Full-text search
176
+ linearis issues read API-123 # Get issue details
177
+ linearis issues update API-123 --status "Done" # Close issue
178
+ linearis issues update API-123 --status "In Progress"
179
+ linearis issues update API-123 --assignee user123
180
+ linearis issues update API-123 --labels "Bug" --label-by adding
181
+ linearis issues create --title "Fix it" --team UAT --priority 2
182
+ linearis comments create API-123 --body "Fixed in PR #456"
183
+ linearis teams list
184
+ linearis users list --active
185
+ linearis projects list
186
+ linearis documents list
187
+ linearis usage # Full command reference
188
+ ```
189
+
190
+ All output is JSON, suitable for piping to `jq`.
23
191
 
24
192
  ## Prerequisites
25
193
 
26
- - OpenClaw gateway running (systemd service)
27
- - A Linear workspace with API access
28
- - A Linear OAuth application (Settings > API > Applications)
29
- - A public URL for webhook delivery (e.g., Cloudflare tunnel)
194
+ - **OpenClaw** gateway running (v2026.2+)
195
+ - **Linear** workspace with API access
196
+ - **Public URL** for webhook delivery (Cloudflare Tunnel recommended)
197
+ - **Coding CLIs** (at least one): `codex`, `claude`, `gemini` — installed in PATH
198
+ - **linearis** CLI — for issue management
199
+
200
+ ## Install
201
+
202
+ ```bash
203
+ openclaw plugins install @calltelemetry/openclaw-linear
204
+ ```
30
205
 
31
206
  ## Setup
32
207
 
33
- ### 1. Create a Linear OAuth Application
208
+ ### 1. Create a Linear OAuth App
209
+
210
+ Go to **Linear Settings > API > Applications** and create a new application:
211
+
212
+ - **Webhook URL:** `https://<your-domain>/linear/webhook`
213
+ - **Redirect URI:** `https://<your-domain>/linear/oauth/callback`
214
+ - Enable webhook events: **Agent Sessions**, **Comments**, **Issues**
34
215
 
35
- 1. Go to **Linear Settings > API > Applications**
36
- 2. Click **Create new application**
37
- 3. Fill in:
38
- - **Application name:** your agent's name
39
- - **Redirect URI:** `https://<your-domain>/linear/oauth/callback`
40
- - **Webhook URL:** `https://<your-domain>/linear/webhook`
41
- 4. Note the **Client ID** and **Client Secret**
42
- 5. Enable the webhook events you need (Agent Sessions, Issues)
216
+ Save the **Client ID** and **Client Secret**.
43
217
 
44
- ### 2. Create a Workspace Webhook
218
+ ### 2. Set Credentials
45
219
 
46
- Separately from the OAuth app, create a workspace-level webhook:
220
+ Add to your gateway's environment (systemd service or shell):
47
221
 
48
- 1. Go to **Linear Settings > API > Webhooks**
49
- 2. Create a new webhook pointing to `https://<your-domain>/linear/webhook`
50
- 3. Enable these event types: **Comment**, **Issue**, **User**
222
+ ```bash
223
+ export LINEAR_CLIENT_ID="your_client_id"
224
+ export LINEAR_CLIENT_SECRET="your_client_secret"
225
+ ```
51
226
 
52
- > **Why two webhooks?** The OAuth app webhook handles `AgentSessionEvent` and `AppUserNotification` events (agent-specific). The workspace webhook handles `Comment` and `Issue` events (workspace-wide). Both must point to the same URL — the plugin routes internally.
227
+ For systemd:
53
228
 
54
- ### 3. Expose the Gateway via Cloudflare Tunnel
229
+ ```ini
230
+ [Service]
231
+ Environment=LINEAR_CLIENT_ID=your_client_id
232
+ Environment=LINEAR_CLIENT_SECRET=your_client_secret
233
+ ```
55
234
 
56
- The OpenClaw gateway listens on `localhost:<port>` (default `18789`). Linear must reach it over HTTPS to deliver webhooks. A [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) is the recommended approach — no open inbound ports, no self-managed TLS.
235
+ Then reload: `systemctl --user daemon-reload && systemctl --user restart openclaw-gateway`
57
236
 
58
- #### Install `cloudflared`
237
+ ### 3. Expose the Gateway
238
+
239
+ Linear needs to reach your gateway over HTTPS to deliver webhooks. A [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) is the recommended approach — no open ports, no TLS certificates to manage.
240
+
241
+ #### a. Install `cloudflared`
59
242
 
60
243
  ```bash
61
244
  # RHEL / Rocky / Alma
62
245
  sudo dnf install -y cloudflared
63
246
 
64
247
  # Debian / Ubuntu
65
- curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
66
- echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflared.list
67
- sudo apt update && sudo apt install -y cloudflared
248
+ sudo apt install -y cloudflared
68
249
 
69
250
  # macOS
70
251
  brew install cloudflare/cloudflare/cloudflared
71
252
  ```
72
253
 
73
- #### Authenticate with Cloudflare
254
+ #### b. Authenticate with Cloudflare
74
255
 
75
256
  ```bash
76
257
  cloudflared tunnel login
77
258
  ```
78
259
 
79
- This opens your browser to Cloudflare's authorization page. You must:
80
-
81
- 1. Log in to your Cloudflare account
82
- 2. **Select the domain** (zone) you want the tunnel to use (e.g., `yourdomain.com`)
83
- 3. Click **Authorize**
260
+ This opens your browser. Log in, select the domain you want to use, and click **Authorize**.
84
261
 
85
- Cloudflare writes an origin certificate to `~/.cloudflared/cert.pem`. This certificate grants `cloudflared` permission to create tunnels and DNS records under that domain. Without it, tunnel creation will fail.
86
-
87
- #### Create a tunnel
262
+ #### c. Create a tunnel
88
263
 
89
264
  ```bash
90
265
  cloudflared tunnel create openclaw
91
266
  ```
92
267
 
93
- This outputs a **Tunnel ID** (a UUID like `da1f21bf-856e-...`) and writes a credentials file to `~/.cloudflared/<TUNNEL_ID>.json`.
268
+ Note the **Tunnel ID** (a UUID) from the output.
94
269
 
95
- #### Create a DNS subdomain for the tunnel
270
+ #### d. Point a subdomain at the tunnel
96
271
 
97
272
  ```bash
98
273
  cloudflared tunnel route dns openclaw linear.yourdomain.com
99
274
  ```
100
275
 
101
- This creates a **CNAME record** in your Cloudflare DNS:
102
-
103
- ```
104
- linear.yourdomain.com CNAME <TUNNEL_ID>.cfargotunnel.com
105
- ```
106
-
107
- You can verify it in the Cloudflare dashboard under **DNS > Records** for your domain. The subdomain (`linear.yourdomain.com`) is what Linear will use for webhook delivery and OAuth callbacks.
276
+ This creates a DNS record so `linear.yourdomain.com` routes through the tunnel.
108
277
 
109
- > **Important:** Your domain must already be on Cloudflare (nameservers pointed to Cloudflare). If it's not, add it in the Cloudflare dashboard first.
110
-
111
- #### Configure the tunnel
278
+ #### e. Configure the tunnel
112
279
 
113
280
  Create `~/.cloudflared/config.yml`:
114
281
 
115
282
  ```yaml
116
283
  tunnel: <TUNNEL_ID>
117
- credentials-file: /home/<user>/.cloudflared/<TUNNEL_ID>.json
284
+ credentials-file: ~/.cloudflared/<TUNNEL_ID>.json
118
285
 
119
286
  ingress:
120
287
  - hostname: linear.yourdomain.com
@@ -122,22 +289,21 @@ ingress:
122
289
  - service: http_status:404
123
290
  ```
124
291
 
125
- The `ingress` rule routes all traffic for your subdomain to the OpenClaw gateway on localhost. The catch-all `http_status:404` rejects requests for any other hostname.
126
-
127
- #### Run as a systemd service
292
+ #### f. Start the tunnel
128
293
 
129
294
  ```bash
295
+ # Install as a system service (starts on boot)
130
296
  sudo cloudflared service install
131
297
  sudo systemctl enable --now cloudflared
132
298
  ```
133
299
 
134
- This installs a system-level service that starts on boot. To test without installing:
300
+ To test without installing as a service:
135
301
 
136
302
  ```bash
137
303
  cloudflared tunnel run openclaw
138
304
  ```
139
305
 
140
- #### Verify end-to-end
306
+ #### g. Verify the tunnel
141
307
 
142
308
  ```bash
143
309
  curl -s https://linear.yourdomain.com/linear/webhook \
@@ -146,228 +312,105 @@ curl -s https://linear.yourdomain.com/linear/webhook \
146
312
  # Should return: "ok"
147
313
  ```
148
314
 
149
- > **Note:** The hostname you choose here (`linear.yourdomain.com`) is what you'll use for the OAuth redirect URI and both webhook URLs in Linear. Make sure they all match.
315
+ ### 4. Authorize with Linear
150
316
 
151
- ### 4. Set Environment Variables
152
-
153
- Required:
154
317
  ```bash
155
- export LINEAR_CLIENT_ID="your_client_id"
156
- export LINEAR_CLIENT_SECRET="your_client_secret"
318
+ openclaw openclaw-linear auth
157
319
  ```
158
320
 
159
- Optional:
160
- ```bash
161
- export LINEAR_REDIRECT_URI="https://your-domain.com/linear/oauth/callback"
162
- export OPENCLAW_GATEWAY_PORT="18789" # if non-default
163
- ```
321
+ This opens your browser to authorize the agent. The plugin needs these OAuth scopes:
164
322
 
165
- ### 5. Install the Plugin
323
+ | Scope | What it enables |
324
+ |---|---|
325
+ | `read` / `write` | Read and update issues, post comments |
326
+ | `app:assignable` | Agent appears in Linear's assignment menus |
327
+ | `app:mentionable` | Users can @mention the agent in comments |
166
328
 
167
- If you haven't already installed via [Quick Install](#quick-install):
329
+ After authorization, restart the gateway:
168
330
 
169
331
  ```bash
170
- openclaw plugins install @calltelemetry/openclaw-linear
171
- openclaw gateway restart
332
+ systemctl --user restart openclaw-gateway
172
333
  ```
173
334
 
174
- This registers the plugin in your OpenClaw config and restarts the gateway to load it.
175
-
176
- <details>
177
- <summary>Manual config (advanced)</summary>
178
-
179
- If you prefer to manage config by hand, add the plugin path to `~/.openclaw/openclaw.json`:
180
-
181
- ```json
182
- {
183
- "plugins": {
184
- "load": {
185
- "paths": ["/path/to/linear"]
186
- },
187
- "entries": {
188
- "linear": {
189
- "enabled": true
190
- }
191
- }
192
- }
193
- }
194
- ```
195
-
196
- Then restart: `openclaw gateway restart`
197
- </details>
198
-
199
- ### 6. Run the OAuth Flow
200
-
201
- There are two ways to authorize the plugin with Linear.
202
-
203
- #### Option A: CLI Flow (Recommended)
335
+ Verify it's working:
204
336
 
205
337
  ```bash
206
- openclaw auth linear oauth
338
+ openclaw openclaw-linear status
207
339
  ```
208
340
 
209
- This launches the OAuth flow interactively:
210
-
211
- 1. The plugin constructs the authorization URL with the required scopes
212
- 2. Your browser opens to Linear's authorization page
213
- 3. You approve the permissions
214
- 4. Linear redirects to the callback URL with an authorization code
215
- 5. The plugin exchanges the code for access + refresh tokens
216
- 6. Tokens are stored in `~/.openclaw/auth-profiles.json`
217
-
218
- #### Option B: Manual URL
219
-
220
- If the CLI flow doesn't work (headless server, tunnel issues), construct the URL yourself:
221
-
222
- ```
223
- https://linear.app/oauth/authorize
224
- ?client_id=YOUR_CLIENT_ID
225
- &redirect_uri=https://your-domain.com/linear/oauth/callback
226
- &response_type=code
227
- &scope=read,write,app:assignable,app:mentionable
228
- &state=random_string
229
- &actor=app
230
- ```
231
-
232
- Key parameters:
233
-
234
- | Parameter | Value | Why |
235
- |---|---|---|
236
- | `scope` | `read,write,app:assignable,app:mentionable` | `app:assignable` lets the agent appear in assignment menus. `app:mentionable` lets users @mention the agent. |
237
- | `actor` | `app` | Makes the token act as the **application identity**, not a personal user. Agent sessions require this. |
238
- | `redirect_uri` | Your callback URL | Must match what you registered in the OAuth app settings. |
239
-
240
- Click the URL, authorize in Linear, and the callback handler at `/linear/oauth/callback` will exchange the code and store the tokens automatically.
341
+ You should see `token: profile` in the gateway logs.
241
342
 
242
- #### What Gets Stored
343
+ ### 5. Configure Agents
243
344
 
244
- After a successful OAuth flow, `~/.openclaw/auth-profiles.json` will contain:
245
-
246
- ```json
247
- {
248
- "version": 1,
249
- "profiles": {
250
- "linear:default": {
251
- "type": "oauth",
252
- "provider": "linear",
253
- "accessToken": "...",
254
- "refreshToken": "...",
255
- "expiresAt": 1708109280000,
256
- "scope": "app:assignable app:mentionable read write"
257
- }
258
- }
259
- }
260
- ```
261
-
262
- This file should be `chmod 600` (owner-only). The plugin auto-refreshes tokens 60 seconds before expiry and persists the new tokens back to this file.
263
-
264
- ### 7. Configure Agent Profiles
265
-
266
- Create `~/.openclaw/agent-profiles.json` to define role-based agents:
345
+ Create `~/.openclaw/agent-profiles.json` to define your agent team:
267
346
 
268
347
  ```json
269
348
  {
270
349
  "agents": {
271
350
  "lead": {
272
351
  "label": "Lead",
273
- "mission": "Product owner. Sets direction, makes scope decisions, prioritizes backlog.",
352
+ "mission": "Product owner. Sets direction, prioritizes backlog.",
274
353
  "isDefault": true,
275
- "mentionAliases": ["lead", "product"],
276
- "appAliases": ["myagent"],
277
- "avatarUrl": "https://example.com/lead-avatar.png"
354
+ "mentionAliases": ["lead"],
355
+ "avatarUrl": "https://example.com/lead.png"
278
356
  },
279
357
  "qa": {
280
358
  "label": "QA",
281
- "mission": "Test engineer. Quality guardian, test strategy, release confidence.",
359
+ "mission": "Test engineer. Quality guardian, test strategy.",
282
360
  "mentionAliases": ["qa", "tester"]
283
361
  },
284
362
  "infra": {
285
363
  "label": "Infra",
286
- "mission": "Backend and infrastructure engineer. Performance, reliability, observability.",
364
+ "mission": "Backend engineer. Performance, reliability, observability.",
287
365
  "mentionAliases": ["infra", "backend"]
288
- },
289
- "ux": {
290
- "label": "UX",
291
- "mission": "User experience advocate. Accessibility, user journeys, pain points.",
292
- "mentionAliases": ["ux", "design"]
293
- },
294
- "docs": {
295
- "label": "Docs",
296
- "mission": "Technical writer. Setup guides, API references, release notes.",
297
- "mentionAliases": ["docs", "writer"]
298
366
  }
299
367
  }
300
368
  }
301
369
  ```
302
370
 
303
- | Field | Required | Description |
304
- |---|---|---|
305
- | `label` | Yes | Display name in Linear comments |
306
- | `mission` | Yes | Agent's role description (injected as system context when the agent is dispatched) |
307
- | `isDefault` | One agent | The default agent handles OAuth app events, agent sessions, and assignment triage |
308
- | `mentionAliases` | Yes | @mention triggers in comments (e.g., `@qa` in a comment routes to the QA agent) |
309
- | `appAliases` | No | Triggers via OAuth app webhook (default agent only, for app-level @mentions) |
310
- | `avatarUrl` | No | Avatar displayed on branded comments. Falls back to `[Label]` prefix if not set. |
371
+ Each agent name must match an agent definition in your `~/.openclaw/openclaw.json`.
311
372
 
312
- #### How agent-profiles.json connects to openclaw.json
373
+ One agent must be marked `isDefault: true` — this is the agent that handles issue assignments and the pipeline.
313
374
 
314
- Each key in `agent-profiles.json` (e.g., `"lead"`, `"qa"`) must have a matching agent definition in your OpenClaw config (`~/.openclaw/openclaw.json`). The Linear plugin dispatches work via `openclaw agent --agent <id>`, so the agent must actually exist.
375
+ ### 6. Configure Coding Tools
315
376
 
316
- Example — if `agent-profiles.json` defines `"lead"` and `"qa"`, your `openclaw.json` needs:
377
+ Create `coding-tools.json` in the plugin root:
317
378
 
318
379
  ```json
319
380
  {
320
- "agents": {
321
- "lead": {
322
- "model": "claude-sonnet-4-5-20250929",
323
- "systemPrompt": "You are a product lead agent...",
324
- "tools": ["linear_list_issues", "linear_create_issue", "linear_add_comment"]
325
- },
326
- "qa": {
327
- "model": "claude-sonnet-4-5-20250929",
328
- "systemPrompt": "You are a QA engineer agent...",
329
- "tools": ["linear_list_issues", "linear_add_comment"]
330
- }
381
+ "codingTool": "codex",
382
+ "agentCodingTools": {},
383
+ "backends": {
384
+ "claude": { "aliases": ["claude", "claude code", "anthropic"] },
385
+ "codex": { "aliases": ["codex", "openai"] },
386
+ "gemini": { "aliases": ["gemini", "google"] }
331
387
  }
332
388
  }
333
389
  ```
334
390
 
335
- #### Routing flow
391
+ ### 7. Install linearis
336
392
 
337
- ```
338
- Linear comment "@qa review this test plan"
339
- Plugin matches "qa" in mentionAliases
340
- Looks up agent-profiles.json → finds "qa" profile
341
- → Dispatches: openclaw agent --agent qa --message "<issue context + comment>"
342
- → OpenClaw loads "qa" agent config from openclaw.json
343
- → Agent runs with the qa profile's mission as context
344
- → Response posted back to Linear as a branded comment with qa's label/avatar
393
+ ```bash
394
+ npm install -g linearis
395
+ npx clawhub install linearis
396
+ echo "lin_api_YOUR_KEY" > ~/.linear_api_token
345
397
  ```
346
398
 
347
- For agent sessions (triggered by the Linear agent UI or app @mentions):
399
+ ### 8. Verify
348
400
 
401
+ ```bash
402
+ systemctl --user restart openclaw-gateway
349
403
  ```
350
- Linear AgentSessionEvent.created
351
- → Plugin resolves the default agent (isDefault: true)
352
- → Runs the 3-stage pipeline (plan → implement → audit)
353
- → Each stage dispatches via the default agent's openclaw.json config
354
- ```
355
-
356
- #### What happens if they don't match
357
404
 
358
- - **Agent profile exists but no matching openclaw.json agent:** The dispatch fails and an error is logged. The webhook returns 200 (Linear requirement) but no comment is posted.
359
- - **openclaw.json agent exists but no profile:** The agent works for direct CLI use but won't be reachable from Linear. No @mention alias maps to it.
360
- - **No agent marked `isDefault`:** Agent sessions and assignment triage will fail with `"No defaultAgentId"` error.
405
+ Check the logs for a clean startup:
361
406
 
362
- ### 8. Verify
363
-
364
- ```bash
365
- openclaw gateway restart
366
- openclaw logs | grep -i linear
367
- # Should show: "Linear agent extension registered (agent: default, token: profile)"
368
407
  ```
408
+ [plugins] Linear agent extension registered (agent: default, token: profile,
409
+ codex: codex-cli 0.101.0, claude: 2.1.45, gemini: 0.28.2, orchestration: enabled)
410
+ ```
411
+
412
+ Test the webhook:
369
413
 
370
- Test the webhook is reachable:
371
414
  ```bash
372
415
  curl -s -X POST https://your-domain.com/linear/webhook \
373
416
  -H "Content-Type: application/json" \
@@ -375,162 +418,93 @@ curl -s -X POST https://your-domain.com/linear/webhook \
375
418
  # Should return: "ok"
376
419
  ```
377
420
 
378
- ## How It Works
421
+ ## Usage
379
422
 
380
- ### Token Resolution
423
+ Once set up, the plugin responds to Linear events automatically:
381
424
 
382
- The plugin resolves an OAuth token from:
383
-
384
- 1. Plugin config `accessToken` (static, for testing)
385
- 2. Auth profile store `linear:default` (from the OAuth flow this is the normal path)
386
-
387
- OAuth is required. The plugin needs `app:assignable` and `app:mentionable` scopes to function — agent sessions, branded comments, assignment triage, and @mention routing all depend on the application identity that only OAuth provides.
388
-
389
- ### Webhook Event Routing
390
-
391
- ```
392
- POST /linear/webhook
393
- |
394
- +-- AgentSessionEvent.created --> 3-stage pipeline (plan -> implement -> audit)
395
- +-- AgentSessionEvent.prompted --> Resume pipeline (user approved plan)
396
- +-- AppUserNotification --> Direct agent response to mention/assignment
397
- +-- Comment.create --> Route @mention to role-based agent
398
- +-- Issue.update --> Triage if assigned/delegated to app user
399
- ```
400
-
401
- All handlers respond `200 OK` within 5 seconds (Linear requirement), then process asynchronously.
425
+ | What you do in Linear | What happens |
426
+ |---|---|
427
+ | Create a new issue | Agent triages it (estimate, labels, priority) and posts an assessment |
428
+ | Assign an issue to the agent | Agent triages and posts assessment |
429
+ | Trigger an agent session | 3-stage pipeline: plan, implement, audit |
430
+ | Comment `@qa check the tests` | QA agent responds with its expertise |
431
+ | Comment `@infra why is this slow` | Infra agent investigates and replies |
432
+ | Ask "close this issue" | Agent runs `linearis issues update API-123 --status Done` |
433
+ | Ask "use gemini to review" | Agent calls `code_run` with `backend: "gemini"` |
402
434
 
403
- ### Pipeline Stages
435
+ ## Configuration Reference
404
436
 
405
- Triggered by `AgentSessionEvent.created`:
437
+ ### Environment Variables
406
438
 
407
- | Stage | Timeout | What It Does |
439
+ | Variable | Required | Description |
408
440
  |---|---|---|
409
- | **Planner** | 5 min | Analyzes issue, generates implementation plan, posts as comment, waits for approval |
410
- | **Implementor** | 10 min | Follows the approved plan, makes changes, creates commits/PRs |
411
- | **Auditor** | 5 min | Reviews implementation against plan, posts audit report |
412
-
413
- The auditor stage can be disabled via plugin config: `"enableAudit": false`.
414
-
415
- ### Assignment Triage
441
+ | `LINEAR_CLIENT_ID` | Yes | OAuth app client ID |
442
+ | `LINEAR_CLIENT_SECRET` | Yes | OAuth app client secret |
443
+ | `LINEAR_API_KEY` | No | Personal API key (fallback if no OAuth) |
444
+ | `LINEAR_REDIRECT_URI` | No | Override the OAuth callback URL |
445
+ | `OPENCLAW_GATEWAY_PORT` | No | Gateway port (default: 18789) |
416
446
 
417
- When an issue is assigned or delegated to the app user:
447
+ ### Plugin Config
418
448
 
419
- 1. Fetches full issue details and available team labels
420
- 2. Dispatches the default agent with a triage prompt
421
- 3. Agent returns JSON with story point estimate and label IDs
422
- 4. Plugin applies the estimate and labels to the issue
423
- 5. Posts the assessment as a branded comment
449
+ Optional overrides in `openclaw.json` under the plugin entry:
424
450
 
425
- ### @Mention Routing
451
+ | Key | Type | Default | Description |
452
+ |---|---|---|---|
453
+ | `defaultAgentId` | string | — | Override which agent handles pipeline/triage |
454
+ | `enableAudit` | boolean | `true` | Run the auditor stage after implementation |
455
+ | `enableOrchestration` | boolean | `true` | Allow agents to use `spawn_agent`/`ask_agent` |
456
+ | `codexBaseRepo` | string | `/home/claw/ai-workspace` | Git repo path for Codex worktrees |
457
+ | `codexModel` | string | — | Default Codex model |
458
+ | `codexTimeoutMs` | number | `600000` | Default timeout for coding CLIs |
426
459
 
427
- When a comment contains `@qa`, `@infra`, or any configured `mentionAliases`:
460
+ ### Coding Tools Config (`coding-tools.json`)
428
461
 
429
- 1. Plugin matches the alias to an agent profile
430
- 2. Reacts with eyes emoji to acknowledge
431
- 3. Fetches full issue context (description, recent comments, labels, state)
432
- 4. Dispatches the matched agent with the comment context
433
- 5. Posts the agent's response as a branded comment on the issue
462
+ | Key | Type | Default | Description |
463
+ |---|---|---|---|
464
+ | `codingTool` | string | `"claude"` | Default coding backend |
465
+ | `agentCodingTools` | object | `{}` | Per-agent backend overrides (`agentId → backendId`) |
466
+ | `backends` | object | `{}` | Per-backend config (aliases, etc.) |
467
+ | `backends.*.aliases` | string[] | `[backendId]` | Alias names that resolve to this backend |
434
468
 
435
- The default agent's `mentionAliases` are excluded from comment routing — the default agent is reached via `appAliases` through the OAuth app webhook instead.
469
+ ### Agent Profile Fields
436
470
 
437
- ### Comment Deduplication
438
-
439
- Webhook events are deduplicated for 60 seconds using a key based on:
440
- - Comment ID (for `Comment.create`)
441
- - Session ID (for `AgentSessionEvent`)
442
- - Assignment tuple (for `Issue.update`)
443
-
444
- ## Plugin Config Schema
445
-
446
- Optional settings in `openclaw.json` under the plugin entry:
447
-
448
- ```json
449
- {
450
- "plugins": {
451
- "entries": {
452
- "linear": {
453
- "enabled": true,
454
- "clientId": "...",
455
- "clientSecret": "...",
456
- "redirectUri": "...",
457
- "accessToken": "...",
458
- "defaultAgentId": "...",
459
- "enableAudit": true
460
- }
461
- }
462
- }
463
- }
464
- ```
465
-
466
- All fields are optional — environment variables and auth profiles are the preferred configuration method.
467
-
468
- ## HTTP Routes
469
-
470
- | Route | Method | Purpose |
471
+ | Field | Required | Description |
471
472
  |---|---|---|
472
- | `/linear/webhook` | POST | Primary webhook endpoint |
473
- | `/hooks/linear` | POST | Backward-compatible webhook endpoint |
474
- | `/linear/oauth/callback` | GET | OAuth authorization callback |
475
-
476
- ## Agent Tools
473
+ | `label` | Yes | Display name shown on comments in Linear |
474
+ | `mission` | Yes | Role description (injected as context when the agent runs) |
475
+ | `isDefault` | One agent | Handles issue triage and the pipeline |
476
+ | `mentionAliases` | Yes | `@mention` triggers (e.g., `["qa", "tester"]`) |
477
+ | `avatarUrl` | No | Avatar for branded comments |
477
478
 
478
- Agents have access to these Linear tools during execution:
479
+ ### CLI
479
480
 
480
- | Tool | Description |
481
- |---|---|
482
- | `linear_list_issues` | List issues (with optional team filter) |
483
- | `linear_create_issue` | Create a new issue |
484
- | `linear_add_comment` | Add a comment to an issue |
485
-
486
- ## File Structure
487
-
488
- ```
489
- linear/
490
- ├── index.ts # Entry point, registers routes and provider
491
- ├── openclaw.plugin.json # Plugin metadata and config schema
492
- ├── package.json # Package definition (zero runtime deps)
493
- ├── README.md
494
- └── src/
495
- ├── agent.ts # Agent dispatch via openclaw CLI
496
- ├── auth.ts # OAuth provider registration and token refresh
497
- ├── client.ts # Basic GraphQL client (for agent tools)
498
- ├── linear-api.ts # Full GraphQL API wrapper (LinearAgentApi)
499
- ├── oauth-callback.ts # OAuth callback handler
500
- ├── pipeline.ts # 3-stage pipeline (plan -> implement -> audit)
501
- ├── tools.ts # Agent tools (list, create, comment)
502
- ├── webhook.ts # Webhook dispatcher (5 event handlers)
503
- └── webhook.test.ts # Tests (vitest)
481
+ ```bash
482
+ openclaw openclaw-linear auth # Run OAuth authorization
483
+ openclaw openclaw-linear status # Check connection and token status
504
484
  ```
505
485
 
506
486
  ## Troubleshooting
507
487
 
508
- **Plugin not loading:**
488
+ Quick checks:
489
+
509
490
  ```bash
510
- openclaw doctor --fix
511
- openclaw logs | grep -i "linear\|plugin\|error"
491
+ systemctl --user status openclaw-gateway # Is the gateway running?
492
+ openclaw openclaw-linear status # Is the token valid?
493
+ journalctl --user -u openclaw-gateway -f # Watch live logs
494
+ linearis issues list -l 1 # Is linearis authenticated?
512
495
  ```
513
496
 
514
- **Webhook not receiving events:**
515
- - Verify both webhooks (workspace + OAuth app) point to the same URL
516
- - Check that your tunnel/proxy is forwarding to the gateway port
517
- - Linear requires `200 OK` within 5 seconds — check for gateway latency
497
+ ### Common Issues
518
498
 
519
- **Agent sessions not working:**
520
- - OAuth tokens require `app:assignable` and `app:mentionable` scopes
521
- - Personal API keys cannot create agent sessions use OAuth
522
- - Re-run `openclaw auth linear oauth` to get fresh tokens
523
-
524
- **"No defaultAgentId" error:**
525
- - Set `defaultAgentId` in plugin config, OR
526
- - Mark one agent as `"isDefault": true` in `agent-profiles.json`
499
+ | Problem | Cause | Fix |
500
+ |---|---|---|
501
+ | Agent says "closing" but doesn't | No issue management tool available | Install `linearis` skill: `npx clawhub install linearis` |
502
+ | `code_run` uses wrong backend | Default/per-agent config mismatch | Check `coding-tools.json` |
503
+ | Claude Code "nested session" error | `CLAUDECODE` env var set | Plugin handles this automatically (unsets the var) |
504
+ | Gateway rejects plugin config keys | Strict validator in `openclaw.json` | Custom config goes in `coding-tools.json`, not `openclaw.json` |
505
+ | Webhook events not arriving | Wrong webhook URL | Both workspace and OAuth app webhooks must point to `/linear/webhook` |
506
+ | OAuth token expired | Tokens expire ~24h | Auto-refreshes via refresh token; restart gateway if stuck |
527
507
 
528
- **Token refresh failures:**
529
- - Ensure `LINEAR_CLIENT_ID` and `LINEAR_CLIENT_SECRET` are set
530
- - Check that the refresh token in `auth-profiles.json` hasn't been revoked
531
- - Re-run the OAuth flow to get new tokens
508
+ ## License
532
509
 
533
- **OAuth callback not working:**
534
- - Verify the redirect URI in Linear's app settings matches your gateway URL
535
- - If behind a reverse proxy, ensure `X-Forwarded-Proto` and `Host` headers are forwarded
536
- - For local dev, the callback defaults to `http://localhost:<gateway-port>/linear/oauth/callback`
510
+ MIT