Metadata-Version: 2.4
Name: takopi-linear
Version: 0.1.0
Summary: Takopi transport backend for Linear Agent SDK
Project-URL: Source, https://github.com/asianviking/takopi-linear
Project-URL: Issues, https://github.com/asianviking/takopi-linear/issues
Keywords: linear,takopi
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.14
Requires-Dist: anyio>=4.0
Requires-Dist: httpx>=0.27
Requires-Dist: psycopg[binary]>=3.2
Requires-Dist: pydantic>=2.0
Requires-Dist: takopi>=0.1
Provides-Extra: test
Requires-Dist: pytest-anyio>=0.0.0; extra == 'test'
Requires-Dist: pytest>=8.0; extra == 'test'
Description-Content-Type: text/markdown

# takopi-linear

Linear transport backend plugin for [takopi](https://github.com/kaiships/takopi). Turns Linear into a full project management interface for your AI coding agent — delegate issues, track progress through native Agent Activities, and get PRs back.

**Status:** pre-alpha / early development. Not recommended for production use yet.

## How it works

```
┌─────────────┐     webhook      ┌──────────────────┐     Neon DB      ┌─────────────────┐
│   Linear    │ ──────────────►  │   kai-gateway    │ ──────────────►  │  takopi-linear  │
│  (issues,   │                  │   (Vercel)       │    events table  │  (this plugin)  │
│  comments,  │  ◄────────────── │                  │                  │                 │
│  agent UI)  │   GraphQL API    └──────────────────┘  ◄────────────── │  polls for new  │
└─────────────┘                                          poll + claim  │  events, runs   │
                                                                       │  takopi engine   │
                                                                       └─────────────────┘
```

1. A user creates a Linear issue and **delegates it to the bot** (assigns as agent)
2. Linear fires an `AgentSessionEvent` webhook to **[kai-gateway](https://github.com/asianviking/kai-gateway)**
3. kai-gateway validates the signature, normalizes the event, and inserts it into a shared **Neon (Postgres) database**
4. **takopi-linear** polls the database, claims the event, and starts a takopi session
5. The AI engine (Claude Code, Codex, etc.) does the work — progress streams back to Linear as native Agent Activities (thoughts, plans, actions)
6. When done, the bot creates a PR, transitions the issue to Done, and posts a final response

## Setup guide

### Prerequisites

- Python 3.14+
- A [Neon](https://neon.tech) Postgres database (free tier works)
- A [Vercel](https://vercel.com) account (for deploying kai-gateway)
- A Linear workspace with admin access
- [takopi](https://github.com/kaiships/takopi) installed and configured with at least one engine backend

### Step 1: Create the Neon database

1. Sign up at [neon.tech](https://neon.tech) and create a new project
2. Create a database called `kai_gateway`
3. Run the schema to create the events table:

```sql
CREATE EXTENSION IF NOT EXISTS pgcrypto;

CREATE TABLE events (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    source      TEXT NOT NULL,
    event_type  TEXT NOT NULL,
    external_id TEXT,
    payload     JSONB NOT NULL,
    status      TEXT NOT NULL DEFAULT 'pending',
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    processed_at TIMESTAMPTZ,
    error       TEXT
);

CREATE INDEX idx_events_status ON events (status) WHERE status = 'pending';
CREATE INDEX idx_events_source ON events (source, status);
CREATE UNIQUE INDEX idx_events_external_id ON events (source, external_id) WHERE external_id IS NOT NULL;
```

4. Copy your connection string — you'll need it for both kai-gateway and takopi-linear:
   ```
   postgresql://user:pass@host.neon.tech/kai_gateway?sslmode=require
   ```

### Step 2: Deploy kai-gateway

[kai-gateway](https://github.com/asianviking/kai-gateway) is a lightweight Vercel serverless function that receives Linear webhooks and writes them to the shared Neon database.

1. Clone the repo:
   ```bash
   git clone https://github.com/asianviking/kai-gateway.git
   cd kai-gateway
   ```

2. Deploy to Vercel:
   ```bash
   npx vercel
   ```

3. Set the environment variables in Vercel (Settings > Environment Variables):

   | Variable | Description |
   |----------|-------------|
   | `DATABASE_URL` | Neon connection string from Step 1 |
   | `LINEAR_WEBHOOK_SECRET` | You'll get this from Linear in Step 3 |

4. Note your deployment URL (e.g. `https://kai-gateway.vercel.app`) — you'll need it when configuring the Linear webhook.

### Step 3: Create the Linear bot application

1. Go to **Linear Settings > API > Applications** and create a new application
2. Configure the application:
   - **Name**: Your bot's name (e.g. "Kai")
   - **Actor**: Select `Application` (this gives the bot its own identity)
   - **OAuth scopes**: Enable `app:assignable`, `app:mentionable`, `read`, `write`
3. Install the application to your workspace (requires admin)
4. Copy the **OAuth access token** and **Application ID** — you'll need these for takopi-linear config
5. Set up the webhook:
   - **Webhook URL**: `https://your-kai-gateway.vercel.app/webhooks/linear`
   - **Signing secret**: Generate one and save it — add this to kai-gateway's `LINEAR_WEBHOOK_SECRET` env var
   - **Events**: Enable `Issues`, `Comments`, and `Agent sessions`

### Step 4: Install and configure takopi-linear

1. Install the plugin:
   ```bash
   pip install takopi-linear
   ```

2. Add the Linear transport to your `takopi.toml`:

   ```toml
   transport = "linear"

   [transports.linear]
   oauth_token = "lin_oauth_..."       # from Step 3
   app_id = "your-app-id"              # from Step 3
   gateway_database_url = "postgresql://...@...neon.tech/kai_gateway?sslmode=require"  # from Step 1
   poll_interval = 5.0

   # Map Linear projects to local repos
   [plugins.linear]
   project_map = { "linear-project-uuid" = "my-project" }
   ```

3. Start takopi — it will begin polling the gateway database for new events:
   ```bash
   takopi run
   ```

### Step 5: Test the integration

1. Create a new Linear issue in a mapped project
2. Assign the bot as the agent (delegate to it)
3. The bot should acknowledge within a few seconds with an initial plan
4. Watch it work through the issue and deliver a PR

## Configuration reference

### Transport settings (`[transports.linear]`)

```toml
[transports.linear]
# Required
oauth_token = "..."                          # Linear OAuth access token (actor=app)
app_id = "..."                               # Linear application ID
gateway_database_url = "postgresql://..."     # Neon DB connection string

# Polling
poll_interval = 5.0                          # seconds between polls (default: 5.0)
poll_batch_size = 10                         # events per poll (default: 10)

# Message formatting
message_overflow = "split"                   # "split" or "trim" (default: "split")
max_body_chars = 10000                       # split threshold (default: 10000)
plan_coalesce_s = 1.0                        # debounce plan updates (default: 1.0)
ephemeral_activity_coalesce_s = 0.2          # debounce thought updates (default: 0.2)

# Attachments
download_attachments = true                  # download attached files from prompts
attachment_allowed_hosts = ["uploads.linear.app"]
attachment_max_bytes = 20000000              # 20 MB
attachment_dir = ".takopi-linear/attachments"
```

### Direct webhook server (optional)

For lower latency, you can also run a direct webhook server alongside DB polling. Events are deduplicated across both paths.

```toml
[transports.linear]
webhook_enabled = true
webhook_host = "0.0.0.0"
webhook_port = 8080
webhook_path = "/webhooks/linear"
webhook_signing_secret = "..."               # HMAC-SHA256 signing secret
```

### Plugin settings (`[plugins.linear]`)

```toml
[plugins.linear]
# Project mapping (required — maps Linear project IDs to takopi project aliases)
project_map = { "linear-project-id" = "backend" }

# Worktrees
worktree_strategy = "per_issue"              # "per_issue" (default) or "shared"
branch_prefix = "kai/"                       # branch prefix (default: "kai/")

# Workflow transitions (optional)
success_state_name = "In Review"             # case-insensitive; falls back to first type="completed"
# success_state_names = ["Ready for Review", "In Review"]  # first match wins

# Auto-labels (opt-in)
auto_labels_enabled = false
auto_labels = { processed = "agent:processed", completed = "agent:completed", failed = "agent:failed" }

# Sub-issues (opt-in — create sub-issues from checklist items)
subissues_enabled = false
subissues_max = 5

# Comment follow-ups (treat new comments as continuation prompts)
comment_followups_enabled = true
comment_followups_debounce_seconds = 0.5

# Prioritization
event_prioritization_enabled = true          # sort by issue priority + cycle
current_cycle_only = false                   # skip issues outside current cycle

# Metadata
estimate_updates_enabled = false             # update issue estimates

# Engine/model overrides per project
project_models = { "backend" = "opus" }      # default model per project
project_reasoning = { "backend" = "high" }   # default reasoning level per project
project_engines = { "backend" = "claude" }   # default engine per project
```

### File transfer (`[transports.linear.files]`)

```toml
[transports.linear.files]
enabled = true
auto_put = true
auto_put_mode = "prompt"                     # "upload" = save only, "prompt" = save + annotate prompt
uploads_dir = "incoming"
```

## Usage

### Delegating work

Create a Linear issue and assign the bot as the agent. The issue title becomes the prompt:

| Issue title | Branch | Prompt |
|-------------|--------|--------|
| `Add JWT authentication` | `kai/AV-123` (auto) | "Add JWT authentication" |
| `@feat/auth Add JWT authentication` | `feat/auth` (custom) | "Add JWT authentication" |

### Title directives

Override model, reasoning, or engine per issue using brackets in the title:

```
[model:sonnet] Fix the login bug
[reasoning:high] Refactor the auth module
[engine:codex] Scaffold the new API
```

Or use Linear labels: `model > sonnet`, `engine > codex`, `reasoning > high`.

### Special commands

Use these in issue comments:

| Command | Effect |
|---------|--------|
| `stop`, `cancel` | Cancel the current run |
| `/new`, `/reset` | Clear session context and start fresh |
| `/help`, `help`, `?` | Show help text |
| `/file put <path>` | Upload an attached file to the working directory |
| `/file get <path>` | Download a file (or zipped directory) back to Linear |

### Worktrees / branching

By default (`worktree_strategy = "per_issue"`), each issue runs in its own git worktree with a branch derived from the issue identifier (e.g. `kai/AV-123`).

To override per issue, prefix the title with `@branch/name` (e.g. `@fix/login Fix the login bug`).

## kai-gateway

[kai-gateway](https://github.com/asianviking/kai-gateway) is the webhook ingress layer that sits between Linear and takopi-linear. It's a simple Vercel serverless function that:

1. Receives Linear webhook events at `POST /webhooks/linear`
2. Verifies the HMAC-SHA256 signature
3. Checks for replay attacks (rejects stale deliveries)
4. Normalizes the event payload
5. Inserts it into the shared Neon `events` table with deduplication

takopi-linear then polls this table, atomically claims pending events using `FOR UPDATE SKIP LOCKED`, and processes them. This decoupled architecture means:

- **No public endpoint needed on the takopi host** — it only makes outbound connections
- **Multiple consumers** can safely poll the same table (events won't be double-processed)
- **Events are durable** — if takopi is down, events queue up and are processed when it comes back

### Environment variables (kai-gateway)

| Variable | Description |
|----------|-------------|
| `DATABASE_URL` | Neon Postgres connection string |
| `LINEAR_WEBHOOK_SECRET` | HMAC-SHA256 signing secret (must match Linear app webhook config) |

## Development

```bash
# Clone and install
git clone https://github.com/asianviking/takopi-linear.git
cd takopi-linear
uv sync --extra test

# Run tests
uv run pytest

# Build
uv build
```

See `RELEASING.md` for the release process.
