Writing automations
defineAutomation, triggers, the AutomationContext, and how dependencies are installed.
defineAutomation
A file exports one default defineAutomation result:
import { defineAutomation } from "@pragma-sh/automations";
export default defineAutomation({
name: "Stale branch sweep", // required
description: "Comment on worktrees idle for 14 days", // required
trigger: { type: "cron", schedule: "0 12 * * 5" },
run: async (ctx, payload) => {
// payload is the trigger's payload, or undefined
},
});Validation is strict — the sidecar rejects a file whose default export does not carry
the pragmaAutomation marker, or whose name/description are empty, whose trigger
is not cron or event, or whose run is not a function.
Triggers
cron
trigger: { type: "cron", schedule: "0 9 * * 1-5" }Standard cron syntax, evaluated by the server on a 20-second tick. The computed
nextRunAt shows in Settings → Automations.
event
trigger: {
type: "event",
listen: (ctx, fire) => {
const timer = setInterval(() => fire({ reason: "tick" }), 60_000);
return () => clearInterval(timer); // dispose — called on unload/shutdown
},
},
run: (ctx, payload) => { /* ... */ },listen registers the trigger and returns an optional dispose function. Call fire
whenever the event happens; each call invokes run with the payload. Disposals run when
the automation is unloaded, reloaded, or when the sidecar shuts down (stdin EOF — the
signal the supervisor uses even after an abrupt kill, so always clean up).
The context
interface AutomationContext {
log: AutomationLogger; // info / warn / error — surfaced in the app
paths: {
project: string; // the automation's project root (or global dir)
worktree: string; // same as project today
global: boolean; // true for ~/.pragma automations
};
fs: {
find(path, { name?, minBytes? }): Promise<string[]>; // bounded file search
};
git: Record<string, never>; // reserved for future use
}log lines appear in the automation's status in the app. fs.find skips generated
directories (node_modules, .git, .pragma, target, …) and is bounded — use it to
locate files without walking the whole tree yourself.
Dependencies
Bare imports (anything that is not a builtin, a relative path, or @pragma-sh/automations
itself) are installed automatically with bun add into a managed cache when the
automation loads. Imports of @pragma-sh/automations are rewritten to the runtime shim —
your file is loaded from a cache copy, and a source change produces a new cache version.
Two practical rules:
- Keep imports narrow; every bare import is installed at load time.
- The source file is capped at 2 MiB; larger files are reported truncated.
What automations can do
Anything the host can: shell out to pragma-cli, call the gateway with @pragma-sh/sdk
(the terminal environment's PRAGMA_* variables are the sidecar's — read
Getting started for the client construction), or write
files. An automation that needs user visibility should publish a scratchpad:
pragma-cli scratchpad create --title "Stale branches" report.mdx