# Linear

> Managing Linear issues, projects, and teams. Use when working with Linear tasks, creating issues, updating status, querying projects, or managing team workflows.

- Skill: `dashed/linear` (Agent Skill)
- Install (CLI): `npx skillmds@latest add dashed/linear`
- Raw SKILL.md: https://api.skillmd.com/api/skills/dashed/linear/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: dashed (https://skillmd.com/u/dashed)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/dashed/linear

---


# Linear

Tools and workflows for managing issues, projects, and teams in Linear.

---

## ⚠️ Tool Availability (READ FIRST)

**This skill supports multiple tool backends. Use whichever is available:**

1. **MCP Tools** (`mcp__linear-server__*` or `mcp__linear__*`, depending on the configured server name) - Use if available in your tool set
2. **Linear CLI (`linear` command)** - Always available via Bash
3. **Helper Scripts** - For complex operations

**If MCP tools are NOT available**, use the Linear CLI via Bash:

```bash
# View an issue
linear issues view ENG-123

# Create an issue
linear issues create --title "Issue title" --description "Description"

# Update issue status (get state IDs first)
linear issues update ENG-123 -s "STATE_ID"

# Add a comment
linear issues comment add ENG-123 -m "Comment text"

# List issues
linear issues list
```

**Do NOT report "MCP tools not available" as a blocker** - use CLI instead.

---

## 🔐 Security: Varlock Integration

**CRITICAL**: Never expose API keys in terminal output or Claude's context.

### Safe Commands (Always Use)

```bash
# Validate LINEAR_API_KEY is set (masked output)
varlock load 2>&1 | grep LINEAR

# Run commands with secrets injected
varlock run -- npx tsx scripts/query.ts "query { viewer { name } }"

# Check schema (safe - no values)
cat .env.schema | grep LINEAR
```

### Unsafe Commands (NEVER Use)

```bash
# ❌ NEVER - exposes key to Claude's context
linear config show
echo $LINEAR_API_KEY
printenv | grep LINEAR
cat .env
```

### Setup for New Projects

1. Create `.env.schema` with `@sensitive` annotation:
   ```bash
   # @type=string(startsWith=lin_api_) @required @sensitive
   LINEAR_API_KEY=
   ```

2. Add `LINEAR_API_KEY` to `.env` (never commit this file)

3. Note: the official MCP server authenticates via OAuth and does not read `LINEAR_API_KEY` — the key is only used by this skill's SDK/GraphQL scripts (`scripts/query.ts`, `scripts/linear-ops.ts`)

4. Use `varlock load` to validate before operations

---

## Quick Start (First-Time Users)

### 1. Check Your Setup

Run the setup check to verify your configuration:

```bash
npx tsx scripts/setup.ts
```

This will check:
- LINEAR_API_KEY is set and valid
- @linear/sdk is installed
- Linear CLI availability (optional)
- MCP configuration (optional)

### 2. Get API Key (If Needed)

If setup reports a missing API key:

1. Open [Linear](https://linear.app) in your browser
2. Go to **Settings** (gear icon) -> **Security & access** -> **Personal API keys**
3. Click **Create key** and copy the key (starts with `lin_api_`)
4. Add to your environment:

```bash
# Option A: Add to shell profile (~/.zshrc or ~/.bashrc)
export LINEAR_API_KEY="lin_api_your_key_here"

# Option B: Add to Claude Code environment
echo 'LINEAR_API_KEY=lin_api_your_key_here' >> ~/.claude/.env

# Then reload your shell or restart Claude Code
```

### 3. Test Connection

Verify everything works:

```bash
npx tsx scripts/query.ts "query { viewer { name } }"
```

You should see your name from Linear.

### 4. Common Operations

```bash
# Create issue in a project
npx tsx scripts/linear-ops.ts create-issue "Project" "Title" "Description"

# Update issue status
npx tsx scripts/linear-ops.ts status Done ENG-123 ENG-124

# Create sub-issue
npx tsx scripts/linear-ops.ts create-sub-issue ENG-100 "Sub-task" "Details"

# Update project status
npx tsx scripts/linear-ops.ts project-status "Phase 1" completed

# Show all commands
npx tsx scripts/linear-ops.ts help
```

See [Project Management Commands](#project-management-commands) for full reference.

---

## Project Planning Workflow

### Create Issues in the Correct Project from the Start

**Best Practice**: When planning a new phase or initiative, create the project and its issues together in a single planning session. Avoid creating issues in a catch-all project and moving them later.

#### Recommended Workflow

1. **Create the project first** (pass `--team` in multi-team workspaces — the default is the workspace's first team):
   ```bash
   npx tsx scripts/linear-ops.ts create-project "Phase X: Feature Name" "My Initiative" --team ENG
   ```

2. **Set project state to Planned**:
   ```bash
   npx tsx scripts/linear-ops.ts project-status "Phase X: Feature Name" planned
   ```

3. **Create issues directly in the project**:
   ```bash
   npx tsx scripts/linear-ops.ts create-issue "Phase X: Feature Name" "Parent task" "Description"
   npx tsx scripts/linear-ops.ts create-sub-issue ENG-XXX "Sub-task 1" "Description"
   npx tsx scripts/linear-ops.ts create-sub-issue ENG-XXX "Sub-task 2" "Description"
   ```

4. **Update project state when work begins**:
   ```bash
   npx tsx scripts/linear-ops.ts project-status "Phase X: Feature Name" in-progress
   ```

#### Why This Matters

- **Traceability**: Issues are linked to their project from creation
- **Metrics**: Project progress tracking is accurate from day one
- **Workflow**: No time wasted moving issues between projects
- **Organization**: Linear views and filters work correctly

#### Anti-Pattern to Avoid

❌ Creating issues in a "holding" project and moving them later:
```bash
# Don't do this
create-issue "Phase 6A" "New feature"  # Wrong project
# Later: manually move to Phase X      # Extra work
```

---

## Project Management Commands

### project-status

Update a project's state in Linear. Accepts user-friendly terminology that maps to Linear's API.

```bash
npx tsx scripts/linear-ops.ts project-status <project-name> <state>
```

**Valid States:**
| Input | Description | API Value |
|-------|-------------|-----------|
| `backlog` | Not yet started | backlog |
| `planned` | Scheduled for future | planned |
| `in-progress` | Currently active | started |
| `paused` | Temporarily on hold | paused |
| `completed` | Successfully finished | completed |
| `canceled` | Will not be done | canceled |

**Examples:**
```bash
# Start working on a project
npx tsx scripts/linear-ops.ts project-status "Phase 8: MCP Decision Engine" in-progress

# Mark project complete
npx tsx scripts/linear-ops.ts project-status "Phase 8" completed

# Partial name matching works
npx tsx scripts/linear-ops.ts project-status "Phase 8" paused
```

### link-initiative

Link an existing project to an initiative.

```bash
npx tsx scripts/linear-ops.ts link-initiative <project-name> <initiative-name>
```

**Examples:**
```bash
# Link a project to an initiative
npx tsx scripts/linear-ops.ts link-initiative "Phase 8: MCP Decision Engine" "Q1 Goals"

# Partial matching works
npx tsx scripts/linear-ops.ts link-initiative "Phase 8" "Q1 Goals"
```

### unlink-initiative

Remove a project from an initiative.

```bash
npx tsx scripts/linear-ops.ts unlink-initiative <project-name> <initiative-name>
```

**Examples:**
```bash
# Remove incorrect link
npx tsx scripts/linear-ops.ts unlink-initiative "Phase 8" "Linear Skill"

# Clean up test links
npx tsx scripts/linear-ops.ts unlink-initiative "Test Project" "Q1 Goals"
```

**Error Handling:**
- Returns error if project is not linked to the specified initiative
- Returns error if project or initiative not found

### Complete Project Lifecycle Example

```bash
# 1. Create project linked to initiative
npx tsx scripts/linear-ops.ts create-project "Phase 11: New Feature" "Q1 Goals"

# 2. Set state to planned
npx tsx scripts/linear-ops.ts project-status "Phase 11" planned

# 3. Create issues in the project
npx tsx scripts/linear-ops.ts create-issue "Phase 11" "Parent task" "Description"
npx tsx scripts/linear-ops.ts create-sub-issue ENG-XXX "Sub-task 1" "Details"

# 4. Start work - update to in-progress
npx tsx scripts/linear-ops.ts project-status "Phase 11" in-progress

# 5. Mark issues done
npx tsx scripts/linear-ops.ts status Done ENG-XXX ENG-YYY

# 6. Complete project
npx tsx scripts/linear-ops.ts project-status "Phase 11" completed

# 7. (Optional) Link to additional initiative
npx tsx scripts/linear-ops.ts link-initiative "Phase 11" "Q2 Goals"
```

---

## Tool Selection

Three tiers, in order. Reach for the lowest tier that fits:

| Tier | Tool | When to Use |
|------|------|-------------|
| 1 | **MCP tools** (official server) | Interactive, one-off operations in conversation — view/create/update a single issue, quick searches. The interactive default. |
| 2 | **SDK scripts** (`npx tsx` + `@linear/sdk`) | The **programmatic default**: bulk, scripted, multi-step, conditional, or repeatable work — anything the API supports, fully typed. See [sdk.md](sdk.md). |
| 3 | **Raw GraphQL** via the SDK's `client.client.rawRequest` | Rare escape hatch only. The SDK is generated from the same GraphQL schema, so "not supported by the SDK" is almost never true. |

The bundled scripts (`scripts/linear-ops.ts`, `scripts/sync.ts`, etc.) are Tier-2 SDK scripts — use them, or write your own, for anything beyond a single interactive action. `scripts/query.ts` is the Tier-3 CLI for ad-hoc raw GraphQL.

### MCP Server Configuration

**Use the official Linear MCP server** at `mcp.linear.app` over native HTTP transport:

```bash
claude mcp add --transport http --scope user linear-server https://mcp.linear.app/mcp
```

Or in `.mcp.json`:

```json
{
  "mcpServers": {
    "linear-server": {
      "type": "http",
      "url": "https://mcp.linear.app/mcp"
    }
  }
}
```

The server authenticates via **OAuth** — run `/mcp`, pick the server, and log in through the browser. `LINEAR_API_KEY` is not used by the MCP connection; it is only needed for this skill's SDK/GraphQL scripts.

> **WARNING**: The old `/sse` endpoint and the `npx mcp-remote https://mcp.linear.app/sse` shim are deprecated — OAuth against `/sse` now fails with "Protected resource https://mcp.linear.app/mcp does not match expected ...". Do NOT use deprecated community servers either. See [troubleshooting.md](troubleshooting.md) for details.

### MCP Reliability (Official Server)

| Operation | Reliability | Notes |
|-----------|-------------|-------|
| Create issue | ✅ High | Full support |
| Update status | ✅ High | Use `state: "Done"` directly |
| List/Search issues | ✅ High | Supports filters, queries |
| Add comment | ✅ High | Works with issue IDs |

### Quick Status Update

```bash
# Via MCP - use human-readable state names (the official server consolidates
# create/update into save_* tools; there is no update_issue)
save_issue with id="issue-uuid", state="Done"

# Via SDK script (bulk operations) - takes a state name then issue identifiers
# (bare numbers like 123 work too; the prefix is stripped automatically)
npx tsx scripts/linear-ops.ts status Done ENG-123 ENG-124
```

### Helper Script Reference

For detailed helper script usage, see **[troubleshooting.md](troubleshooting.md)**.

### Parallel Agent Execution

For bulk operations or background execution, use the `Linear-specialist` subagent:

```javascript
Task({
  description: "Update Linear issues",
  prompt: "Mark ENG-101, ENG-102, ENG-103 as Done",
  subagent_type: "Linear-specialist"
})
```

**When to use `Linear-specialist` (parallel):**
- Bulk status updates (3+ issues)
- Project status changes
- Creating multiple issues
- Sync operations after code changes

**When to use direct execution:**
- Single issue queries
- Viewing issue details
- Quick status checks
- Operations needing immediate results

See **[sync.md](sync.md)** for parallel execution patterns.

## Image Uploads

### Step 1: Extract the image from conversation context

Images shared inline in Claude Code are **not** saved to disk automatically — they live as base64 in the session JSONL. Use the extraction script:

```bash
# Find the current session JSONL
ls -t ~/.claude/projects/<project-path>/*.jsonl | head -1

# Extract all inline images (saves to /tmp by default)
npx tsx scripts/extract-image.ts <path-to-session.jsonl>

# Or specify a custom output directory
npx tsx scripts/extract-image.ts <path-to-session.jsonl> ~/Desktop
```

This saves images to `/tmp/shared-image-0.png`, `/tmp/shared-image-1.png`, etc.

> **Always verify** the extracted image with the Read tool before uploading.

### Step 2: Create the issue

```bash
# Standard approach
npx tsx scripts/linear-ops.ts create-issue "Project Name" "Issue title" "Description"
```

> **Note**: `create-issue` resolves the team from the target project, so it picks the right team automatically. If you still need manual control over the team, use a short SDK call instead of the helper — look up the project's team, then create the issue with an explicit `teamId`:
>
> ```typescript
> // npx tsx in a small script, or inline. @linear/sdk is fully typed.
> import { LinearClient } from '@linear/sdk'
> const client = new LinearClient({ apiKey: process.env.LINEAR_API_KEY })
>
> // Get the project's team
> const projects = await client.projects({ filter: { name: { containsIgnoreCase: 'PROJECT NAME' } } })
> const project = projects.nodes[0]
> const teamId = (await project.teams()).nodes[0].id
>
> // Create with explicit teamId
> await client.createIssue({ teamId, projectId: project.id, title: 'Issue title' })
> ```

### Step 3: Upload the image and attach to the issue

```bash
npx tsx scripts/upload-image.ts /tmp/shared-image-0.png ENG-123 "Optional comment text"
```

The script will:
1. Upload the file to Linear's S3 storage
2. Post a comment on the issue with the image embedded as markdown

**Supported formats**: PNG, JPG/JPEG, GIF, WebP, SVG, PDF

### Known pitfalls

| Problem | Cause | Fix |
|---------|-------|-----|
| `create-issue` picks wrong team | Need a non-default team in a multi-team workspace | Resolve the team from the project via SDK and pass an explicit `teamId` (see Step 2) |
| `upload-image.ts` "Issue not found" | Issue was deleted before attaching | Ensure issue exists first |
| Image not found on disk | Shared inline, not as file | Extract from session JSONL (Step 1) |

---

## Critical Requirements

### Issues → Projects → Initiatives

**Every issue MUST be attached to a project. Every project MUST be linked to an initiative.**

| Entity | Must Link To | If Missing |
|--------|--------------|------------|
| Issue | Project | Not visible in project board |
| Project | Initiative | Not visible in roadmap |

See **[projects.md](projects.md)** for complete project creation checklist.

---

## Conventions

### Issue Status

- **Assigned to me**: Set `state: "Todo"`
- **Unassigned**: Set `state: "Backlog"`

### Labels

Uses **domain-based label taxonomy**. See [docs/labels.md](docs/labels.md).

**Key rules:**
- ONE Type label: `feature`, `bug`, `refactor`, `chore`, `spike`
- 1-2 Domain labels: `security`, `backend`, `frontend`, etc.
- Scope labels when applicable: `blocked`, `breaking-change`, `tech-debt`

```bash
# Validate labels
npx tsx scripts/linear-ops.ts labels validate "feature,security"

# Suggest labels for issue
npx tsx scripts/linear-ops.ts labels suggest "Fix XSS vulnerability"
```

## SDK Automation Scripts

**The right tool for anything programmatic, bulk, or repeatable** — use MCP for interactive single operations, and SDK scripts (`npx tsx` + `@linear/sdk`) for everything scripted. For loops, mapping, conditionals, or bulk updates, write TypeScript using `@linear/sdk`. See [sdk.md](sdk.md) for:

- Complete script patterns and templates
- Common automation examples (bulk updates, filtering, reporting)
- Tool selection criteria

Scripts provide full type hints and are easier to debug than raw GraphQL for multi-step operations.

## GraphQL API

**Fallback only — and not a separate toolchain.** Raw GraphQL is reached *through* the SDK's `client.client.rawRequest` escape hatch, not a parallel set of tools. The bundled `scripts/query.ts` is exactly that: a thin CLI wrapper around `rawRequest`. Use it only for the rare query the typed SDK doesn't already cover.

See **[api.md](api.md)** for complete documentation including:
- Authentication and setup
- The `rawRequest` escape hatch and example queries/mutations
- Timeout handling patterns
- Shell quoting compatibility

**Quick ad-hoc query:**

```bash
npx tsx scripts/query.ts "query { viewer { name } }"
```

## Projects & Initiatives

For advanced project and initiative management patterns, see **[projects.md](projects.md)**.

**Quick reference** - common project commands:

```bash
# Create project linked to initiative (--team takes a team key or name)
npx tsx scripts/linear-ops.ts create-project "Phase X: Name" "My Initiative" --team ENG

# Update project status
npx tsx scripts/linear-ops.ts project-status "Phase X" in-progress
npx tsx scripts/linear-ops.ts project-status "Phase X" completed

# Link/unlink projects to initiatives
npx tsx scripts/linear-ops.ts link-initiative "Phase X" "My Initiative"
npx tsx scripts/linear-ops.ts unlink-initiative "Phase X" "Old Initiative"
```

**Key topics in projects.md:**
- Project creation checklist (mandatory steps)
- Content vs Description fields
- Discovery before creation
- Codebase verification before work
- Sub-issue management
- Project status updates
- Project updates (status reports)

---

## Sync Patterns (Bulk Operations)

For bulk synchronization of code changes to Linear, see **[sync.md](sync.md)**.

**Quick sync commands:**

```bash
# Bulk update issues to Done
npx tsx scripts/linear-ops.ts status Done ENG-101 ENG-102 ENG-103

# Update project status
npx tsx scripts/linear-ops.ts project-status "My Project" completed
```

---

## Reference

| Document | Purpose |
|----------|---------|
| [api.md](api.md) | GraphQL API reference, timeout handling |
| [sdk.md](sdk.md) | SDK automation patterns |
| [sync.md](sync.md) | Bulk sync patterns |
| [projects.md](projects.md) | Project & initiative management |
| [troubleshooting.md](troubleshooting.md) | Common issues, MCP debugging |
| [docs/labels.md](docs/labels.md) | Label taxonomy |

**External:** [Linear MCP Documentation](https://linear.app/docs/mcp.md)
