@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 +339 -365
- package/index.ts +50 -2
- package/openclaw.plugin.json +7 -1
- package/package.json +3 -2
- package/src/active-session.ts +66 -0
- package/src/agent.ts +173 -1
- package/src/auth.ts +6 -2
- package/src/claude-tool.ts +280 -0
- package/src/cli-shared.ts +75 -0
- package/src/cli.ts +39 -0
- package/src/client.ts +1 -0
- package/src/code-tool.ts +202 -0
- package/src/codex-tool.ts +240 -0
- package/src/codex-worktree.ts +264 -0
- package/src/gemini-tool.ts +238 -0
- package/src/orchestration-tools.ts +134 -0
- package/src/pipeline.ts +68 -10
- package/src/tools.ts +29 -79
- package/src/webhook.ts +321 -90
package/README.md
CHANGED
|
@@ -1,120 +1,287 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @calltelemetry/openclaw-linear
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
##
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
- **
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
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
|
-
##
|
|
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
|
-
|
|
17
|
-
|
|
158
|
+
npx clawhub install linearis
|
|
159
|
+
npm install -g linearis
|
|
18
160
|
```
|
|
19
161
|
|
|
20
|
-
|
|
162
|
+
### Auth
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
echo "lin_api_..." > ~/.linear_api_token
|
|
166
|
+
```
|
|
21
167
|
|
|
22
|
-
|
|
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 (
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
218
|
+
### 2. Set Credentials
|
|
45
219
|
|
|
46
|
-
|
|
220
|
+
Add to your gateway's environment (systemd service or shell):
|
|
47
221
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
222
|
+
```bash
|
|
223
|
+
export LINEAR_CLIENT_ID="your_client_id"
|
|
224
|
+
export LINEAR_CLIENT_SECRET="your_client_secret"
|
|
225
|
+
```
|
|
51
226
|
|
|
52
|
-
|
|
227
|
+
For systemd:
|
|
53
228
|
|
|
54
|
-
|
|
229
|
+
```ini
|
|
230
|
+
[Service]
|
|
231
|
+
Environment=LINEAR_CLIENT_ID=your_client_id
|
|
232
|
+
Environment=LINEAR_CLIENT_SECRET=your_client_secret
|
|
233
|
+
```
|
|
55
234
|
|
|
56
|
-
|
|
235
|
+
Then reload: `systemctl --user daemon-reload && systemctl --user restart openclaw-gateway`
|
|
57
236
|
|
|
58
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
268
|
+
Note the **Tunnel ID** (a UUID) from the output.
|
|
94
269
|
|
|
95
|
-
####
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
315
|
+
### 4. Authorize with Linear
|
|
150
316
|
|
|
151
|
-
### 4. Set Environment Variables
|
|
152
|
-
|
|
153
|
-
Required:
|
|
154
317
|
```bash
|
|
155
|
-
|
|
156
|
-
export LINEAR_CLIENT_SECRET="your_client_secret"
|
|
318
|
+
openclaw openclaw-linear auth
|
|
157
319
|
```
|
|
158
320
|
|
|
159
|
-
|
|
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
|
-
|
|
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
|
-
|
|
329
|
+
After authorization, restart the gateway:
|
|
168
330
|
|
|
169
331
|
```bash
|
|
170
|
-
|
|
171
|
-
openclaw gateway restart
|
|
332
|
+
systemctl --user restart openclaw-gateway
|
|
172
333
|
```
|
|
173
334
|
|
|
174
|
-
|
|
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
|
|
338
|
+
openclaw openclaw-linear status
|
|
207
339
|
```
|
|
208
340
|
|
|
209
|
-
|
|
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
|
-
|
|
343
|
+
### 5. Configure Agents
|
|
243
344
|
|
|
244
|
-
|
|
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,
|
|
352
|
+
"mission": "Product owner. Sets direction, prioritizes backlog.",
|
|
274
353
|
"isDefault": true,
|
|
275
|
-
"mentionAliases": ["lead"
|
|
276
|
-
"
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
373
|
+
One agent must be marked `isDefault: true` — this is the agent that handles issue assignments and the pipeline.
|
|
313
374
|
|
|
314
|
-
|
|
375
|
+
### 6. Configure Coding Tools
|
|
315
376
|
|
|
316
|
-
|
|
377
|
+
Create `coding-tools.json` in the plugin root:
|
|
317
378
|
|
|
318
379
|
```json
|
|
319
380
|
{
|
|
320
|
-
"
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
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
|
-
|
|
391
|
+
### 7. Install linearis
|
|
336
392
|
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
421
|
+
## Usage
|
|
379
422
|
|
|
380
|
-
|
|
423
|
+
Once set up, the plugin responds to Linear events automatically:
|
|
381
424
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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
|
-
|
|
435
|
+
## Configuration Reference
|
|
404
436
|
|
|
405
|
-
|
|
437
|
+
### Environment Variables
|
|
406
438
|
|
|
407
|
-
|
|
|
439
|
+
| Variable | Required | Description |
|
|
408
440
|
|---|---|---|
|
|
409
|
-
|
|
|
410
|
-
|
|
|
411
|
-
|
|
|
412
|
-
|
|
413
|
-
|
|
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
|
-
|
|
447
|
+
### Plugin Config
|
|
418
448
|
|
|
419
|
-
|
|
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
|
-
|
|
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
|
-
|
|
460
|
+
### Coding Tools Config (`coding-tools.json`)
|
|
428
461
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
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
|
-
|
|
469
|
+
### Agent Profile Fields
|
|
436
470
|
|
|
437
|
-
|
|
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
|
-
|
|
|
473
|
-
|
|
|
474
|
-
|
|
|
475
|
-
|
|
476
|
-
|
|
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
|
-
|
|
479
|
+
### CLI
|
|
479
480
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
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
|
-
|
|
488
|
+
Quick checks:
|
|
489
|
+
|
|
509
490
|
```bash
|
|
510
|
-
openclaw
|
|
511
|
-
openclaw
|
|
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
|
-
|
|
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
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|