Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
407 changes: 49 additions & 358 deletions plugins/agentdev/skills/debug-mode/SKILL.md

Large diffs are not rendered by default.

4 changes: 1 addition & 3 deletions plugins/agentdev/skills/xml-standards/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
---
name: xml-standards
description: XML tag structure patterns for Claude Code agents and commands. Use when designing or implementing agents to ensure proper XML structure following Anthropic best practices.
description: "Specifies XML tag structure patterns (role, expertise, constraints, workflow) for Claude Code agents and commands following Anthropic best practices. Provides required and optional tag schemas with nesting rules. Use when designing agent prompts, implementing XML-structured commands, or validating agent tag compliance."
---
plugin: agentdev
updated: 2026-01-20

# XML Tag Standards

Expand Down
7 changes: 1 addition & 6 deletions plugins/agentdev/skills/yaml-agent-format/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
---
name: yaml-agent-format
description: YAML format for Claude Code agent definitions as alternative to markdown. Use when creating agents with YAML, converting markdown agents to YAML, or validating YAML agent schemas. Trigger keywords - "YAML agent", "agent YAML", "YAML format", "agent schema", "YAML definition", "convert to YAML".
version: 0.1.0
tags: [agentdev, yaml, agent, format, schema, definition]
keywords: [yaml, agent, format, schema, definition, conversion, validation, frontmatter]
plugin: agentdev
updated: 2026-01-28
description: "Defines the YAML schema for Claude Code agent definitions as an alternative to markdown. Covers required fields, validation rules, and markdown-to-YAML conversion patterns. Use when creating agents in YAML format, converting existing markdown agents to YAML, or validating YAML agent schemas."
---

# YAML Agent Format
Expand Down
253 changes: 56 additions & 197 deletions plugins/autopilot/skills/linear-integration/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,278 +1,137 @@
---
name: linear-integration
description: Linear API patterns and examples for autopilot. Includes authentication, webhooks, issue CRUD, state transitions, file attachments, and comment handling.
version: 0.1.0
tags: [linear, api, webhook, integration]
keywords: [linear, api, webhook, issue, comment, state, attachment]
description: "Provides Linear API patterns for authentication, webhook handling, issue CRUD, state transitions, file attachments, and comment posting. Includes signature verification and SDK usage examples. Use when integrating with Linear API or handling Linear webhook events."
---
plugin: autopilot
updated: 2026-01-20

# Linear Integration

**Version:** 0.1.0
**Purpose:** Patterns for Linear API integration in autopilot workflows
**Status:** Phase 1
Provides patterns and examples for integrating with the Linear API in autopilot workflows, covering authentication, webhooks, issue management, and state transitions.

## When to Use
## Workflow

Use this skill when you need to:
- Authenticate with Linear API
- Set up webhook handlers for Linear events
- Create, read, update, or delete Linear issues
- Transition issue states in Linear workflows
- Attach files to Linear issues
- Add comments to Linear issues

## Overview

This skill provides patterns for:
- Linear API authentication
- Webhook handler setup
- Issue CRUD operations
- State transitions
- File attachments
- Comment handling
1. **Authenticate** - initialize LinearClient with API key
2. **Set up webhook handler** - configure HTTP server with signature verification
3. **Route events** - process incoming webhook payloads by action type
4. **Execute operations** - create/read/update issues, transition states, attach files, post comments

## Core Patterns

### Pattern 1: Authentication

**Personal API Key (MVP):**
```typescript
import { LinearClient } from '@linear/sdk';

const linear = new LinearClient({
apiKey: process.env.LINEAR_API_KEY
});
```
const linear = new LinearClient({ apiKey: process.env.LINEAR_API_KEY });

**Verification:**
```typescript
async function verifyConnection(): Promise<boolean> {
try {
const me = await linear.viewer;
console.log(`Connected as: ${me.name}`);
return true;
} catch (error) {
console.error('Linear connection failed:', error);
return false;
}
}
// Verify connection
const me = await linear.viewer;
console.log(`Connected as: ${me.name}`);
```

### Pattern 2: Webhook Handler
### Pattern 2: Webhook Handler with Signature Verification

**Bun HTTP Server:**
```typescript
import { serve } from 'bun';
import { createHmac } from 'crypto';

interface LinearWebhookPayload {
action: 'created' | 'updated' | 'deleted';
type: 'Issue' | 'Comment' | 'Label';
data: {
id: string;
title?: string;
description?: string;
state: { id: string; name: string };
labels: Array<{ id: string; name: string }>;
};
}

serve({
port: process.env.AUTOPILOT_WEBHOOK_PORT || 3001,

async fetch(req: Request): Promise<Response> {
if (req.method !== 'POST') {
return new Response('Method not allowed', { status: 405 });
}
if (req.method !== 'POST') return new Response('Method not allowed', { status: 405 });

// Verify signature
const signature = req.headers.get('Linear-Signature');
const body = await req.text();

if (!verifySignature(body, signature)) {
// Verify HMAC-SHA256 signature
const hmac = createHmac('sha256', process.env.LINEAR_WEBHOOK_SECRET!);
if (signature !== hmac.update(body).digest('hex')) {
return new Response('Unauthorized', { status: 401 });
}

const payload: LinearWebhookPayload = JSON.parse(body);

// Route to handler
await routeWebhook(payload);

await routeWebhook(JSON.parse(body));
return new Response('OK', { status: 200 });
}
});

function verifySignature(body: string, signature: string | null): boolean {
if (!signature) return false;

const hmac = createHmac('sha256', process.env.LINEAR_WEBHOOK_SECRET!);
const expectedSignature = hmac.update(body).digest('hex');

return signature === expectedSignature;
}
```

### Pattern 3: Issue Operations
### Pattern 3: Issue CRUD

**Create Issue:**
```typescript
async function createIssue(
teamId: string,
title: string,
description: string,
labels: string[]
): Promise<string> {
// Note: Linear SDK uses linear.createIssue() method
const result = await linear.createIssue({
teamId,
title,
description,
labelIds: await resolveLabelIds(labels),
assigneeId: process.env.AUTOPILOT_BOT_USER_ID,
priority: 2,
});

const issue = await result.issue;
return issue!.id;
}
```

**Query Issues:**
```typescript
async function getAutopilotTasks(teamId: string) {
const issues = await linear.issues({
filter: {
team: { id: { eq: teamId } },
assignee: { id: { eq: process.env.AUTOPILOT_BOT_USER_ID } },
state: { name: { in: ['Todo', 'In Progress'] } },
},
});

return issues.nodes;
}
// Create issue
const result = await linear.createIssue({
teamId, title, description,
labelIds: await resolveLabelIds(labels),
assigneeId: process.env.AUTOPILOT_BOT_USER_ID,
priority: 2,
});
const issueId = (await result.issue)!.id;

// Query autopilot tasks
const issues = await linear.issues({
filter: {
team: { id: { eq: teamId } },
assignee: { id: { eq: process.env.AUTOPILOT_BOT_USER_ID } },
state: { name: { in: ['Todo', 'In Progress'] } },
},
});
```

### Pattern 4: State Transitions

**Transition State:**
```typescript
async function transitionState(
issueId: string,
newStateName: string
): Promise<void> {
// Get workflow states for the issue's team
async function transitionState(issueId: string, newStateName: string): Promise<void> {
const issue = await linear.issue(issueId);
const team = await issue.team;
const states = await team.states();

const states = await (await issue.team).states();
const targetState = states.nodes.find(s => s.name === newStateName);

if (!targetState) {
throw new Error(`State "${newStateName}" not found`);
}

// Note: Linear SDK uses linear.updateIssue() method
await linear.updateIssue(issueId, {
stateId: targetState.id,
});
if (!targetState) throw new Error(`State "${newStateName}" not found`);
await linear.updateIssue(issueId, { stateId: targetState.id });
}
```

### Pattern 5: File Attachments

**Upload and Attach:**
```typescript
async function attachFile(
issueId: string,
filePath: string,
fileName: string
): Promise<void> {
// Request upload URL
const uploadPayload = await linear.fileUpload(
getMimeType(filePath),
fileName,
getFileSize(filePath)
);

// Upload to storage
const fileContent = await Bun.file(filePath).arrayBuffer();
async function attachFile(issueId: string, filePath: string, fileName: string): Promise<void> {
const uploadPayload = await linear.fileUpload(getMimeType(filePath), fileName, getFileSize(filePath));
await fetch(uploadPayload.uploadUrl, {
method: 'PUT',
body: fileContent,
body: await Bun.file(filePath).arrayBuffer(),
headers: { 'Content-Type': getMimeType(filePath) },
});

// Attach to issue
await linear.attachmentCreate({
issueId,
url: uploadPayload.assetUrl,
title: fileName,
});
await linear.attachmentCreate({ issueId, url: uploadPayload.assetUrl, title: fileName });
}
```

### Pattern 6: Comments

**Add Comment:**
```typescript
async function addComment(
issueId: string,
body: string
): Promise<void> {
// Note: Linear SDK uses linear.createComment() method
await linear.createComment({
issueId,
body,
});
}
await linear.createComment({ issueId, body: "Implementation complete. See attached proof." });
```

## Best Practices

- Always verify webhook signatures
- Use exponential backoff for API rate limits
- Cache team/state/label IDs to reduce API calls
- Handle webhook delivery failures gracefully
- Log all state transitions for audit

## Examples

### Example 1: Full Issue Lifecycle

### Full issue lifecycle
```typescript
// Create issue
const issueId = await createIssue(
teamId,
"Add user profile page",
"Implement user profile with avatar upload",
["frontend", "feature"]
);

// Transition to In Progress
const issueId = await createIssue(teamId, "Add user profile page", "...", ["frontend"]);
await transitionState(issueId, "In Progress");

// ... work happens ...

// Attach proof artifacts
await attachFile(issueId, "screenshot.png", "Desktop Screenshot");

// Add completion comment
await addComment(issueId, "Implementation complete. See attached proof.");

// Transition to In Review
await addComment(issueId, "Implementation complete.");
await transitionState(issueId, "In Review");
```

### Example 2: Query Autopilot Queue

### Query autopilot queue
```typescript
const tasks = await getAutopilotTasks(teamId);

console.log(`Autopilot queue: ${tasks.length} tasks`);
for (const task of tasks) {
console.log(`- ${task.identifier}: ${task.title} (${task.state.name})`);
}
```

## Verification

When using these patterns, confirm:
- [ ] Webhook signatures are verified before processing payloads
- [ ] API rate limits are handled with exponential backoff
- [ ] Team/state/label IDs are cached to reduce API calls
- [ ] All state transitions are logged for audit
Loading