@plotday/twister
v0.33.0
Published
Plot Twist Creator - Build intelligent extensions that integrate and automate
Maintainers
Readme
Quick Start
Choose your path:
- No Code - Write natural language, deploy in minutes
- TypeScript - Full control with code
No-Code Quick Start
Describe your twist and Plot will do the rest.
1. Create plot-twist.md:
# 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 meetings2. Deploy:
npx @plotday/twister login
npx @plotday/twister deployThat's it! Learn more →
Developer Quick Start
Build twists with TypeScript for maximum flexibility.
1. Create a new twist:
npx @plotday/twister create2. Implement your twist:
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:
npm run plot login
npm run deployCore Concepts
Twists
Twists are smart automations that connect, organize, and prioritize your work. They implement opinionated workflows and respond to lifecycle events.
// Lifecycle methods
async activate(priority) // When twist is added to a priority
async deactivate() // When twist is removed
async upgrade() // When new version is deployedTwist Tools
Twist tools provide capabilities to twists. They are usually unopinionated and do nothing on their own. Use built-in tools or create your own.
Built-in Tools:
- Plot - Manage activities and priorities
- Store - Persistent key-value storage
- AI - Language models with structured output
- Integrations - OAuth authentication
- Network - HTTP access and webhooks
- Tasks - Background task execution
- Callbacks - Persistent function references
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 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.
// 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 ✅",
});CLI Commands
# 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 priorityDocumentation
📚 Full Documentation at twist.plot.day
Guides
- Getting Started - Complete walkthrough
- Core Concepts - Twists, tools, and architecture
- Sync Strategies - Data synchronization patterns (upserts, deduplication, ID management)
- Built-in Tools - Plot, Store, AI, and more
- Building Custom Tools - Create reusable twist tools
- Runtime Environment - Execution constraints and optimization
- Advanced Topics - Complex patterns and techniques
Reference
- CLI Reference - Complete command documentation
- API Reference - TypeDoc-generated API docs
Examples
Simple Note Twist
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 Integration
export default class GitHubTwist extends Twist<GitHubTwist> {
build(build: ToolBuilder) {
return {
plot: build(Plot),
network: build(Network, {
urls: ["https://api.github.com/*"],
}),
};
}
async activate(priority: Pick<Priority, "id">) {
// Set up webhook for issue updates
const webhookUrl = await this.tools.network.createWebhook("onIssueUpdate");
await this.set("webhook_url", webhookUrl);
}
async onIssueUpdate(request: WebhookRequest) {
// Sync GitHub issues to Plot activities
}
}TypeScript Configuration
Extend the Twist Creator's base configuration in your tsconfig.json:
{
"extends": "@plotday/twister/tsconfig.base.json",
"include": ["src/*.ts"]
}Support
- Documentation: twist.plot.day
- Issues: github.com/plotday/plot/issues
- Website: plot.day
License
MIT © Plot Technologies Inc.
