This guide will walk you through creating your first Plot Twist. There are two ways to build twists: with natural language (no code) or with TypeScript code for maximum flexibility.
Create twists using natural language descriptions - no programming required!
Create a file named plot-twist.md in your project directory and describe what you want your twist to do:
# My Calendar Twist
I want a twist that:
- Syncs my Google Calendar events into Plot as activities
- Creates tasks for upcoming meetings
- Sends me a reminder 10 minutes before each meeting
- Updates activity status when meetings are completed
Be specific about:
You'll need a Plot account to deploy twists.
# Login to Plot
npx @plotday/twister login
# Deploy directly from your spec
npx @plotday/twister deploy
That's it! Your twist is now live in Plot.
If you want to review or customize the generated code before deploying:
# Generate TypeScript code from your spec
npx @plotday/twister generate
# Review and edit the generated src/index.ts
# Then deploy
npx @plotday/twister deploy
The generate command creates a complete TypeScript twist that you can modify and extend.
Build twists with full control using TypeScript.
Use the Plot CLI to scaffold a new twist:
npx @plotday/twister create
# or
yarn dlx @plotday/twister create
# or
pnpm dlx @plotday/twister create
You'll be prompted for:
my-calendar-twist)This creates a new directory with:
my-calendar-twist/
├── src/
│ └── index.ts # Your twist code
├── package.json
├── tsconfig.json
└── plot-twist.json # Twist configuration
Edit src/index.ts to add your twist logic:
import {
type Activity,
ActivityType,
Twist,
type Priority,
type ToolBuilder,
} from "@plotday/twister";
import { Plot } from "@plotday/twister/tools/plot";
export default class MyTwist extends Twist<MyTwist> {
// Declare tool dependencies
build(build: ToolBuilder) {
return {
plot: build(Plot),
};
}
// Called when the twist is activated for a priority
async activate(priority: Pick<Priority, "id">) {
await this.tools.plot.createActivity({
type: ActivityType.Note,
title: "Welcome! Your twist is now active.",
});
}
// Called when an activity is routed to this twist
async activity(activity: Activity) {
console.log("Processing activity:", activity.title);
}
}
Build and check for errors:
npm run build
# or
pnpm build
You'll need a Plot account to deploy twists.
# Login to Plot
npm run plot login
# Deploy your twist
npm run deploy
Your twist is now deployed and ready to activate in Plot!
Your twist extends the Twist class and implements:
build() - Declares tool dependenciesactivate() - Initialization when added to a prioritydeactivate() - Cleanup when removed from a priorityupgrade() - Migration when deploying a new versionContains twist metadata:
{
"name": "my-calendar-twist",
"displayName": "My Calendar Twist",
"version": "1.0.0",
"description": "Syncs calendar events to Plot"
}
Extends the Twist Creator's base configuration:
{
"extends": "@plotday/twister/tsconfig.base.json",
"include": ["src/*.ts"]
}
Now that you have a basic twist running, explore:
await this.tools.plot.createActivity({
type: ActivityType.Task,
title: "Review pull request",
note: "Check the new authentication flow",
links: [
{
type: ActivityLinkType.external,
title: "View PR",
url: "https://github.com/org/repo/pull/123",
},
],
});
// Save
await this.set("last_sync", new Date().toISOString());
// Retrieve
const lastSync = await this.get<string>("last_sync");
// Run immediately
const callback = await this.callback("processData");
await this.runTask(callback);
// Schedule for later
await this.runTask(callback, {
runAt: new Date("2025-02-01T10:00:00Z"),
});