npm package: @plotday/twister
Description: Plot Twist Creator - Build intelligent extensions that integrate and automate
Author: Plot
Latest version: 0.44.0

<p align="center">
  <a href="https://plot.day" target="_blank" rel="noopener noreferrer">
    <img width="48" height="48" alt="favicon" src="https://github.com/user-attachments/assets/f38501fd-bb37-4671-a0bc-fd35fa25360d" alt="Plot logo" />
  </a>
</p>
<h1 align="center">
  🌪️ Twister, the Plot Twist Creator
</h1>
<p align="center">
  The official package for building <a href="https://plot.day">Plot</a> Twists -<br/>
  smart automations that connect, organize, and prioritize your work.
</p>

<p align="center">
  <a href="https://twist.plot.day"><strong>📚 Full Documentation →</strong></a>
</p>

## Quick Start

Choose your path:

- **[No Code](#no-code-quick-start)** - Write natural language, deploy in minutes
- **[TypeScript](#developer-quick-start)** - Full control with code

### No-Code Quick Start

Describe your twist and Plot will do the rest.

**1. Create `plot-twist.md`:**

```markdown
# My Calendar Twist

I want a twist that:

- Syncs my Google Calendar events into Plot
- Creates tasks for upcoming meetings
- Sends reminders 10 minutes before meetings
```

**2. Deploy:**

```bash
npx @plotday/twister login
npx @plotday/twister deploy
```

That's it! [Learn more →](https://twist.plot.day/documents/Getting_Started.html#no-code-twists)

### Developer Quick Start

Build twists with TypeScript for maximum flexibility.

**1. Create a new twist:**

```bash
npx @plotday/twister create
```

**2. Implement your twist:**

```typescript
import {
  ActivityType,
  type Priority,
  type ToolBuilder,
  Twist,
} from "@plotday/twister";
import { Plot } from "@plotday/twister/tools/plot";

export default class MyTwist extends Twist<MyTwist> {
  build(build: ToolBuilder) {
    return {
      plot: build(Plot),
    };
  }

  async activate(priority: Pick<Priority, "id">) {
    await this.tools.plot.createActivity({
      type: ActivityType.Note,
      title: "Welcome! Your twist is now active.",
      notes: [
        {
          content: "Your twist is ready to use. Check out the [documentation](https://twist.plot.day) to learn more.",
        },
      ],
    });
  }
}
```

**3. Deploy:**

```bash
npm run plot login
npm run deploy
```

[Complete guide →](https://twist.plot.day/documents/Getting_Started.html)

---

## Core Concepts

### Twists

Twists are smart automations that connect, organize, and prioritize your work. They implement opinionated workflows and respond to lifecycle events.

```typescript
// Lifecycle methods
async activate(priority)   // When twist is added to a priority
async deactivate()         // When twist is removed
async upgrade()            // When new version is deployed
```

### Twist Tools

Twist tools provide capabilities to twists. They are usually unopinionated and do nothing on their own.

**Built-in Tools:**

- **Plot** - Manage activities and priorities
- **Store** - Persistent key-value storage
- **AI** - Language models with structured output
- **Integrations** - OAuth authentication and channel lifecycle
- **Network** - HTTP access and webhooks
- **Tasks** - Background task execution
- **Callbacks** - Persistent function references

[View all tools →](https://twist.plot.day/documents/Built-in_Tools.html)

External service integrations (Google Calendar, Slack, Linear, etc.) are built as **Connectors** — see [Building Connectors](https://twist.plot.day/documents/Building_Connectors.html).

### Activities and Notes

**Activity** represents something done or to be done (a task, event, or conversation).
**Notes** represent updates and details on that activity.

Think of an **Activity as a thread** and **Notes as messages in that thread**. Always create activities with an initial note, and add notes for updates rather than creating new activities.

**Data sync:** When syncing from external systems, use `Activity.source` (canonical URL) and `Note.key` for automatic upserts without manual ID tracking. See the [Sync Strategies guide](https://twist.plot.day/documents/Sync_Strategies.html) for detailed patterns.

**Action scheduling:** When creating Actions (tasks), omitting the `start` field defaults to "Do Now" (current time). For most integrations, explicitly set `start: null` to create backlog items ("Do Someday"), only using "Do Now" for urgent or in-progress tasks.

```typescript
// Create an activity with source for automatic deduplication
await this.tools.plot.createActivity({
  source: "https://github.com/org/repo/pull/123", // Enables automatic upserts
  type: ActivityType.Action,
  title: "Review pull request",
  start: null, // "Do Someday" - backlog item (recommended default)
  notes: [
    {
      activity: { source: "https://github.com/org/repo/pull/123" },
      key: "description", // Use key for upsertable notes
      content: "New PR ready for review",
      links: [
        {
          type: ActivityLinkType.external,
          title: "View PR",
          url: "https://github.com/org/repo/pull/123",
        },
      ],
    },
  ],
});

// Add or update a note using key (upserts if key exists)
await this.tools.plot.createNote({
  activity: { source: "https://github.com/org/repo/pull/123" },
  key: "approval", // Using key enables upserts
  content: "LGTM! Approved ✅",
});
```

[Learn more →](https://twist.plot.day/documents/Core_Concepts.html)

---

## CLI Commands

```bash
# Authentication
plot login

# Twist management
plot create                    # Create new twist project
plot generate                  # Generate code from plot-twist.md
plot deploy                    # Deploy to Plot
plot logs                      # Stream real-time twist logs

# Priority management
plot priority list             # List all priorities
plot priority create           # Create new priority
```

[Complete CLI reference →](https://twist.plot.day/documents/CLI_Reference.html)

---

## Documentation

**[📚 Full Documentation at twist.plot.day](https://twist.plot.day)**

### Guides

- [Getting Started](https://twist.plot.day/documents/Getting_Started.html) - Complete walkthrough
- [Core Concepts](https://twist.plot.day/documents/Core_Concepts.html) - Twists, tools, and architecture
- [Sync Strategies](https://twist.plot.day/documents/Sync_Strategies.html) - Data synchronization patterns (upserts, deduplication, ID management)
- [Built-in Tools](https://twist.plot.day/documents/Built-in_Tools.html) - Plot, Store, AI, and more
- [Building Connectors](https://twist.plot.day/documents/Building_Connectors.html) - Build external service integrations
- [Runtime Environment](https://twist.plot.day/documents/Runtime_Environment.html) - Execution constraints and optimization
- [Advanced Topics](https://twist.plot.day/documents/Advanced.html) - Complex patterns and techniques

### Reference

- [CLI Reference](https://twist.plot.day/documents/CLI_Reference.html) - Complete command documentation
- [API Reference](https://twist.plot.day) - TypeDoc-generated API docs

---

## Examples

### Simple Note Twist

```typescript
export default class WelcomeTwist extends Twist<WelcomeTwist> {
  build(build: ToolBuilder) {
    return { plot: build(Plot) };
  }

  async activate(priority: Pick<Priority, "id">) {
    await this.tools.plot.createActivity({
      type: ActivityType.Note,
      title: "Welcome to Plot! 👋",
      notes: [
        {
          content: "This twist will help you get started with Plot.",
        },
      ],
    });
  }
}
```

### GitHub Connector

```typescript
import { Connector, type ConnectorBuilder, type Channel } from "@plotday/twister";
import { Integrations } from "@plotday/twister/tools/integrations";

export default class GitHubConnector extends Connector<GitHubConnector> {
  build(build: ConnectorBuilder) {
    return {
      integrations: build(Integrations),
    };
  }

  async getChannels(): Promise<Channel[]> {
    // Return repositories the user can sync
    return repos.map((repo) => ({
      id: repo.id,
      name: repo.full_name,
    }));
  }

  async onChannelEnabled(channel: Channel) {
    // Start syncing issues from this repository
  }

  async onChannelDisabled(channel: Channel) {
    // Stop syncing and clean up
  }
}
```

[More examples →](https://twist.plot.day/documents/Getting_Started.html)

---

## TypeScript Configuration

Extend the Twist Creator's base configuration in your `tsconfig.json`:

```json
{
  "extends": "@plotday/twister/tsconfig.base.json",
  "include": ["src/*.ts"]
}
```

---

## Support

- **Documentation**: [twist.plot.day](https://twist.plot.day)
- **Issues**: [github.com/plotday/plot/issues](https://github.com/plotday/plot/issues)
- **Website**: [plot.day](https://plot.day)

---

## License

MIT © Plot Technologies Inc.