Skip to content

Build extensions

An extension is a task source: a small program that tells Curupira what work exists. The connectors (GitHub today, Monday.com and Azure DevOps in development) are extensions, and you can write your own for Jira, Linear, a database queue, or anything else that can produce one item at a time.

Extensions run as separate processes. Curupira starts them with uvx or npx and talks to them over stdin and stdout with a small JSON-RPC protocol. They never run inside the Curupira process, and no FFI is involved.

  • Python SDK


    curupira-sdk: decorate a function, return one object. Stdlib-only, Pydantic optional.

    Write a Python extension

  • JavaScript and TypeScript SDK


    @caipora-labs/curupira-sdk: defineTriggers and an npm bin, launched with npx.

    Write a Node.js extension

  • Manifest and protocol


    Framing, methods, results, error codes, and the curupira-extension.toml manifest.

    Read the reference

What an extension does

An extension... Curupira (the host)...
Declares its triggers and the settings each one accepts Reads the TOML and passes each automation's settings
Validates those settings Reports validation errors from curupira validate
Returns the next item as a flat list of key/value pairs, or nothing Deduplicates, schedules, and limits concurrency
Hears when a task starts and when it finishes Clones, creates worktrees, runs setup_script
Writes logs to stderr Renders the prompt and starts the coding agent

Extensions must not choose a coding agent, build agent command lines, clone repositories, or manage worktrees. Coding agents are built into the host; supporting a new agent is a change to Curupira itself.

Register an extension

Every extension is registered in settings.toml under an alias of your choice, with the launcher and its arguments. Automations then address a trigger as <alias>/<trigger>.

[plugins.acme]
type = "uvx"
args = ["--from", "acme-curupira==1.2.0", "acme-curupira"]
tools = ["*"]

[automations.triage]
trigger_type = "acme/tickets"
repository = "api"
prompt = "Triage ${title}"
[plugins.acme]
type = "npx"
args = ["--yes", "@acme/curupira-ext@1.2.0"]
tools = ["*"]

[automations.triage]
trigger_type = "acme/tickets"
repository = "api"
prompt = "Triage ${title}"

args are passed as separate arguments, never through a shell. Pin a version so every machine runs the same code. tools keeps only matching triggers (github-*) or drops them (!github-project-*).

Inspect plugins

curupira plugins lists the triggers you can use and the settings each one accepts:

curupira plugins list
curupira plugins info curupira-plugins

plugins list also shows built-in cron and the built-in coding-agent providers (labelled agent:<name> with origin built-in). Listing reads curupira-extension.toml manifests and never executes extension code.

Runtime requirements

Source Needs on PATH
type = "uvx" plugins uvx (from uv)
type = "npx" plugins npx (Node.js 20+)
Built-in cron and coding agents the agent CLIs you configure, plus git

If a launcher is missing, curupira validate says which one.

Security

Third-party extensions run with your user privileges. A subprocess is not a sandbox. Install only packages you trust, and pin their versions. Curupira never installs packages while it lists plugins.

The text an extension returns usually comes from people outside your team (issue titles, comments). It ends up in a prompt; read task text is untrusted before you automate public sources.