From aa607df13aa32311ab7b4e863350cbc5381a1c65 Mon Sep 17 00:00:00 2001 From: Trevor Hudson Date: Thu, 30 Jul 2026 08:35:42 -0700 Subject: [PATCH] Add rote plugin (Workflow Orchestration) Co-Authored-By: Claude Fable 5 --- .claude-plugin/marketplace.json | 20 ++++ README-zh.md | 1 + README.md | 1 + plugins/rote/.claude-plugin/plugin.json | 23 +++++ plugins/rote/skills/compile/SKILL.md | 131 ++++++++++++++++++++++++ plugins/rote/skills/serve/SKILL.md | 100 ++++++++++++++++++ 6 files changed, 276 insertions(+) create mode 100644 plugins/rote/.claude-plugin/plugin.json create mode 100644 plugins/rote/skills/compile/SKILL.md create mode 100644 plugins/rote/skills/serve/SKILL.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 6f29b6b..99619b9 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -1670,6 +1670,26 @@ "security", "compliance" ] + }, + { + "name": "rote", + "description": "Compile a proven skill (a SKILL.md plus references) into a deterministic pipeline that runs without an LLM in the loop, then serve the compiled pipelines back to Claude as MCP tools.", + "version": "0.12.0", + "author": { + "name": "Trevor Hudson", + "url": "https://github.com/trevhud" + }, + "source": "./plugins/rote", + "category": "Workflow Orchestration", + "homepage": "https://github.com/trevhud/rote", + "keywords": [ + "workflow", + "compilation", + "deterministic", + "temporal", + "dbos", + "mcp" + ] } ] } \ No newline at end of file diff --git a/README-zh.md b/README-zh.md index 2c2de0e..60aee74 100644 --- a/README-zh.md +++ b/README-zh.md @@ -61,6 +61,7 @@ - [lyra](./plugins/lyra) - [model-context-protocol-mcp-expert](./plugins/model-context-protocol-mcp-expert) - [problem-solver-specialist](./plugins/problem-solver-specialist) +- [rote](./plugins/rote) - [studio-coach](./plugins/studio-coach) - [ultrathink](./plugins/ultrathink) diff --git a/README.md b/README.md index e4de615..e551df1 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ Install or disable them dynamically with the `/plugin` command — enabling you - [lyra](./plugins/lyra) - [model-context-protocol-mcp-expert](./plugins/model-context-protocol-mcp-expert) - [problem-solver-specialist](./plugins/problem-solver-specialist) +- [rote](./plugins/rote) - [studio-coach](./plugins/studio-coach) - [ultrathink](./plugins/ultrathink) diff --git a/plugins/rote/.claude-plugin/plugin.json b/plugins/rote/.claude-plugin/plugin.json new file mode 100644 index 0000000..4f06979 --- /dev/null +++ b/plugins/rote/.claude-plugin/plugin.json @@ -0,0 +1,23 @@ +{ + "name": "rote", + "description": "Compile a proven skill (a SKILL.md plus references) into a deterministic pipeline that runs without an LLM in the loop. /rote:compile emits DBOS, Temporal, Cloudflare Workflows, Inngest, or plain Python/TS; /rote:serve exposes the compiled pipelines back to Claude as MCP tools.", + "version": "0.12.0", + "author": { + "name": "Trevor Hudson", + "email": "trevhud@gmail.com" + }, + "homepage": "https://github.com/trevhud/rote", + "repository": "https://github.com/trevhud/rote", + "license": "Apache-2.0", + "keywords": [ + "workflow", + "compilation", + "deterministic", + "temporal", + "dbos", + "cloudflare", + "inngest", + "mcp", + "skills" + ] +} diff --git a/plugins/rote/skills/compile/SKILL.md b/plugins/rote/skills/compile/SKILL.md new file mode 100644 index 0000000..1e3bf81 --- /dev/null +++ b/plugins/rote/skills/compile/SKILL.md @@ -0,0 +1,131 @@ +--- +name: compile +description: >- + Compile an Anthropic-style skill — a directory with a SKILL.md and optional + references/ — into a deterministic, runnable workflow via the rote CLI. Use + when the user says "compile this skill", "graduate this skill" (the retired + name for the same operation), "make this skill deterministic", "make this + skill faster/cheaper", "turn this skill into a workflow", "turn this skill + into code", "harden this skill for production", or complains that a skill + is slow, expensive, or unreliable as a background job. Output: a + pipeline.yaml IR, extracted code modules, typed LLM-judge signatures, and + runtime code for Temporal, Cloudflare Workflows, or DBOS. +--- + +# Compile a skill + +You orchestrate the `rote` CLI. It runs an LLM compiler agent over a +source skill and emits a deterministic pipeline. Your job: resolve the +inputs, run the CLI, then interpret the output for the user. You never +classify nodes or write pipeline.yaml yourself — the CLI's agent does. + +## 1. Identify the source skill + +The source is a **directory containing a `SKILL.md`** (optionally a +`references/` folder). The user names it, or you infer it from context +(a skill just discussed, a path in the conversation, `.claude/skills/*` +or `skills/*` in the project). + +**Confirm the resolved absolute path with the user before running.** +Compilation costs real time and tokens; never guess-and-go. If the +directory has no `SKILL.md`, stop and ask. + +## 2. Pick a runtime target + +Ask the user which runtime, with these tradeoffs (one line each): + +| Runtime | Choose when | Emits | +|---|---|---| +| `dbos` | No infra to run — durability lives in SQLite/Postgres, runs anywhere Python runs | Python | +| `cloudflare` | You want serverless, fully managed execution on Cloudflare Workers | TypeScript | +| `temporal` | You already operate (or want) a Temporal cluster | Python | + +If the user has no opinion and no existing infra, use `dbos` — it is +the CLI's default and the only target with zero standing +infrastructure (you can omit `--runtime` entirely in that case). + +## 3. Resolve the CLI (uv) + +The CLI ships on PyPI as the `rote-cli` package and is run via `uvx` — +no virtualenv, no pip, nothing to install beyond uv itself. The +package's executable is named `rote`, so every invocation is +`uvx --from rote-cli rote `. Do **not** run `uvx rote-cli ...` — +uvx looks for an executable named after the package and the published +wheel doesn't ship one. + +1. Check uv: `uv --version`. If missing, tell the user to install it + with one command, then re-check: + + ```sh + curl -LsSf https://astral.sh/uv/install.sh | sh + ``` + +2. Confirm the CLI resolves: + + ```sh + uvx --from rote-cli rote --version + ``` + +3. Only if the user needs unreleased features (or PyPI is + unreachable), substitute the GitHub source — same CLI, different + origin: + + ```sh + uvx --from git+https://github.com/trevhud/rote rote --version + ``` + +Do **not** clone the repo or build a venv; `uvx` handles isolation. + +## 4. Run the compilation + +```sh +uvx --from rote-cli rote compile --runtime --out +``` + +Pick an out-dir the user will find, e.g. `./compiled/` +next to the source skill. Ensure it does not clobber existing work. + +Set expectations **before** launching — this is not a quick command: + +- It spawns `claude -p` as a subprocess. The driver deliberately + scrubs `ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN` from the child + environment so the run bills against the user's Claude + subscription, not per-token API charges. Do not "fix" auth by + exporting an API key; if the user explicitly wants API billing, + pass `--agent api` instead. +- A realistic skill takes **~13 minutes wall clock and 30-40 agent + turns** (Sonnet, ~$0.70 on subscription). Small skills are faster. +- Therefore **run it in the background** and tell the user you did. + Poll the process and check in rather than blocking the session. + +If the run exits nonzero, check whether `/compiled/pipeline.yaml` +exists anyway — the CLI recovers completed work from transient +subprocess failures and says so in its output. Surface stderr to the +user either way. + +## 5. Report the result + +Read `/compiled/pipeline.yaml` and +`/compiled/compile-report.md`, then summarize: + +1. **Node-kind table** — count nodes per kind and what each kind means + here: + + | Kind | Count | Meaning | + |---|---|---| + | `pure_function` | n | deterministic code, LLM removed | + | `external_call` | n | direct API call with retry/timeout | + | `llm_judge` | n | typed LLM signature (kept, but bounded) | + | `agent_loop` | n | still agentic (genuinely exploratory) | + | `hitl_gate` | n | durable human approval point | + +2. **Codified fraction** — nodes that no longer need an LLM, mandatory + nodes, and what each HITL gate blocks on. +3. **Where things landed** — `/compiled/` (IR, `extracted/`, + `signatures/`, report) and `/runtime//` (the + deployable code). +4. **Next steps** — the `extracted/*` modules are scaffolds that raise + `NotImplementedError`; the user fills in real API client code, then + deploys the runtime output. Once deployed, `rote register` + + `rote serve` expose the pipeline as an MCP tool so Claude can + trigger runs — the `serve` skill in this plugin walks through that. diff --git a/plugins/rote/skills/serve/SKILL.md b/plugins/rote/skills/serve/SKILL.md new file mode 100644 index 0000000..f2c2a6e --- /dev/null +++ b/plugins/rote/skills/serve/SKILL.md @@ -0,0 +1,100 @@ +--- +name: serve +description: >- + Wire a compiled rote pipeline up as an MCP tool so Claude can trigger the + deployed workflow directly. Use when the user says "register my compiled + pipeline", "serve my pipelines over MCP", "trigger the workflow from + Claude", "hook the pipeline up to Claude", or asks what to do after + `rote compile` and deployment. Covers `rote register` and `rote serve` + plus the `claude mcp add` wiring. +--- + +# Serve compiled pipelines as MCP tools + +`rote serve` is one MCP server exposing every registered pipeline as a +callable tool. The flow: + +``` +rote compile → deploy the runtime → rote register → rote serve → call from Claude +``` + +`rote serve` **triggers deployed workflows; it does not host them.** +MCP triggering supports the `dbos` (default), `temporal`, and +`cloudflare` runtimes. + +The CLI ships on PyPI as `rote-cli` with an executable named `rote`, +so every invocation is `uvx --from rote-cli rote ` (never +`uvx rote-cli ...`). For unreleased features, substitute the source: +`uvx --from git+https://github.com/trevhud/rote rote `. + +## 1. Check preconditions + +- A compile output directory exists (contains `compiled/pipeline.yaml`). +- The runtime side is running: for DBOS, the emitted app in worker mode + (`python main.py --serve` or `dbos start`) against the system + database you'll register — enqueued runs sit in status `enqueued` + until that process exists; for Temporal, a worker against the user's + cluster; for Cloudflare, `wrangler deploy` done. If not, stop and + help with that first. + +## 2. Register the pipeline + +```sh +# DBOS (the default). System DB URL: --system-database-url, else +# $DBOS_SYSTEM_DATABASE_URL, else the emitted app's SQLite file +# (derived from /runtime/dbos/main.py). +uvx --from rote-cli rote register + +# Temporal (defaults: localhost:7233, namespace "default", +# task queue = pipeline.name, workflow type = the emitted versioned name) +uvx --from rote-cli rote register --runtime temporal + +# Cloudflare +uvx --from rote-cli rote register --runtime cloudflare \ + --url https://.workers.dev +``` + +This upserts `~/.rote/registry.json`. Re-registering updates in place. +**After re-compiling a changed skill, register again** — the DBOS and +Temporal workflow names are derived from the pipeline content hash and +must stay in sync with the emitted code. + +## 3. Add the MCP server to Claude + +`rote serve` needs the `serve` extra (FastMCP) plus `dbos` when any +registered pipeline runs on DBOS, so the spec includes both: + +```sh +claude mcp add --scope user rote -- uvx --from 'rote-cli[serve,dbos]' rote serve +``` + +For unreleased features, use the GitHub source instead: + +```sh +claude mcp add --scope user rote -- \ + uvx --from 'rote-cli[serve,dbos] @ git+https://github.com/trevhud/rote' rote serve +``` + +Verify with `claude mcp list`. Each registry entry becomes two tools +(three for DBOS): `` (starts a run, returns `{workflow_id, +status: "started"}` immediately — compiled pipelines run minutes to +days), `_status` (polls a run by `workflow_id`), and for DBOS +`_signal` (resumes a run parked at a HITL gate: `workflow_id` + +gate signal name + resume payload — so Claude can deliver approvals +itself). A DBOS run whose status stays `enqueued` means the emitted app +process isn't running against the registered system database. + +## 4. Explain the reconnect caveat + +A running `rote serve` picks up registry changes live — no restart of +the server, ever. But clients differ: + +- **Claude Code** refreshes its tool list on the server's + `list_changed` notification: newly registered pipelines appear + immediately. +- **Claude Desktop and claude.ai** snapshot tools at connect time. A + pipeline registered while they're connected appears only after a + reconnect — restart Desktop or toggle the server off/on; on + claude.ai, re-enable the connector. + +Tell the user this proactively if they plan to use Desktop or claude.ai.