---
title: Author a recipe by asking AI (CLI authoring)
---

# Author a recipe by asking AI (CLI authoring)

## What it is

Omniscio treats **recipes** as multi-step orchestrated workflows — a recipe runs one or more Claude Code sessions in sequence, can pause for your approval mid-flow, can fan out parallel sub-prompts and consolidate them, and can be triggered manually or on a cron schedule. You can author a recipe by hand in the **Recipes** virtual project (Add Recipe → JSON or step-builder UI), **or** you can describe what you want to an external AI — Claude.ai in a browser, ChatGPT on your phone, or a Claude Code session in any project — and it will draft, validate, and save the recipe for you.

The external AI uses the **`omniscio-control` skill**, which reads the auto-delivered token from `~/.amc/cli-token` and POSTs to Omniscio's local control server at `127.0.0.1:19519`. **Every CLI-saved recipe lands in the Omniscio inbox with `approvalStatus: "pending"` and refuses to run until you approve it** — both manual `RECIPE_START` IPC calls and the cron-style scheduler tick loop gate on the approval state, so a malicious or buggy AI cannot silently execute work on your machine.

This page describes the **CLI authoring path** (external AI → HTTP → Omniscio). For the in-app NL drafter that the Omniscio UI uses internally, see [use-recipe-drafter.md](use-recipe-drafter.md). For what recipes do at runtime and the bundled pattern catalog, see [use-recipes.md](use-recipes.md).

## Where to find it

### Where it shows up in Omniscio

- **Inbox**: pending recipe approvals appear under **Recipe authoring approvals** with the recipe's name, step count, and Approve / Reject buttons. Approving flips the status; rejecting hides it. Either way, the row clears from the pending list.
- **Recipes virtual project (sidebar)**: pending recipes show with an amber "Pending approval" badge instead of the usual run button. Approved recipes are runnable; rejected recipes show a red badge until you delete or revise them.
- **Toast notification**: when a CLI agent saves a recipe, the renderer fires `RECIPE_AUTHORING_APPROVAL_NEEDED` push, which surfaces a low-priority toast ("Recipe ‹name› awaiting approval") so you don't have to be staring at the inbox to notice.

## How it behaves

### How to use it

1. **Enable CLI Control once.** Omniscio → Settings → **CLI Control** → toggle on. Omniscio writes your bearer token to `~/.amc/cli-token` (Windows: `%USERPROFILE%\.amc\cli-token`) with hardened file permissions, and starts the local HTTP server on `127.0.0.1:19519`.

2. **Ask an AI in plain English.** In Claude.ai, ChatGPT, or any Claude Code session say something like:
   - "Build me a recipe that audits my newsletter inbox each morning and writes a Markdown digest."
   - "Create a recipe that fans out 5 sub-prompts on a research question, then consolidates them."
   - "In my newsletter audit recipe, change step 2's prompt to include the day-of-week."

   The `omniscio-control` skill auto-activates on trigger phrases like _build me a recipe_, _set up an orchestration_, _add a step to my X recipe_, _audit my Y inbox_, _fan out N sub-prompts_, _tweak the prompt in_.

3. **Answer the skill's questions.** It batches one round of clarifying questions (max 5): what should the recipe do, what triggers it (manual, schedule, message), should any step pause for approval, any prompts to anchor on, any pre-existing recipe to start from. Then it checks the **bundled pattern library** first — if a pattern fits (e.g. `inbox-triage`, `pipeline-expansion`, `fan-out-consolidate`), the skill instantiates from the pattern instead of synthesizing from scratch.

4. **Watch the validation pass.** The skill posts the draft to `POST /recipe/validate` and surfaces any errors back to you. You can either tell it how to fix them or accept its auto-fix proposal.

5. **Approve in the Omniscio inbox.** The skill posts to `POST /recipe/save`. The new recipe appears in your Omniscio inbox under **Recipe authoring approvals** with the recipe's name, step count, and Approve / Reject buttons. **Until you approve, Omniscio refuses to run the recipe** — the `RECIPE_START` IPC handler returns an error and the cron scheduler skips the recipe on every tick. Click **Approve** to arm it.

6. **Iterate without re-creating.** "Tweak step 2's prompt" → the skill calls `GET /recipe/configs/:id`, edits the config, then calls `PUT /recipe/configs/:id`. Behavioral edits (steps, pipelines, parameters, supervisors, limits) reset the recipe back to `pending` so you re-approve. Cosmetic edits (name, description) leave approval as-is.

**Rate limit**: AI-driven mutations are capped at **10/min**, shared across cron jobs, automation rules, recipe drafts, validations, saves, and updates. The skill surfaces 429s when you hit it. Read-only `GET` requests don't count.

### What the approval gate actually blocks

When a recipe's `approvalStatus` is `pending` or `rejected`:

- **`RECIPE_START` IPC** (manual run from the Recipes UI) returns an error envelope. The renderer surfaces "Approve before running".
- **The scheduler tick loop** skips the recipe entirely — even if it has a cron-style schedule attached (e.g. saved as a cron job referencing `recipeConfigId`).
- **Sub-recipe spawns** triggered from another recipe's step also gate on the sub-recipe's approval state.
- **Agent-trigger HTTP** (`POST /recipes/run` on the CLI control server) refuses to dispatch — even if the recipe has `agentTriggerable.enabled: true`. The two gates are orthogonal: the user opts a recipe into agent-firing once via the toggle, but every individual recipe still has to clear the authoring approval gate before any path (manual, scheduled, sub-recipe, agent-trigger) can run it.

Approval is **idempotent**: re-approving an already-approved recipe is a no-op (no extra notification, no state churn). Rejecting flips status to `rejected`; a subsequent behavioral edit (PUT with `steps`/`pipelines`/`parameters`/`supervisors`/`limits` changed) flips it back to `pending` so you can review the revision.

### Operator FAQ

- **"I approved, can I edit later?"** Yes — edit the recipe in the Recipes UI or have the AI PUT a change. Behavioral edits reset approval; cosmetic edits don't. You'll see "Pending approval" again if the change is behavioral.
- **"I rejected by mistake, what now?"** Open the Recipes virtual project, find the recipe, and either revise it (the next behavioral edit flips it back to `pending`) or delete it and re-author from scratch.
- **"What if the AI keeps draft-saving without me approving?"** There's no fixed cap on saved recipes today. If the inbox queue grows uncomfortable, reject or delete the ones you don't want — the cap that DOES exist is on automation rules (20 pending CLI rules), not recipes. Cron jobs are no longer capped by count either — creation is bounded by the CLI server's shared mutation rate limit instead.
- **"Can the AI bypass the gate by sending `approvalStatus: 'approved'`?"** No. The server forces `pending` on every `POST /recipe/save` regardless of body content. On `PUT /recipe/configs/:id`, the server computes the next state from a behavioral diff against the existing recipe and ignores any client-supplied `approvalStatus`. The gate is enforced server-side, not client-side.
- **"Can I see what the AI sent before I approve?"** Yes — the inbox card includes the recipe's name, step count, and a "View JSON" link that shows the full saved config. Reject if anything looks off.

## For agents

### How it works (under the hood)

The CLI control server is implemented by [/src/main/services/cli/cli-server.ts](/src/main/services/cli/cli-server.ts). On startup it binds `127.0.0.1:19519` and writes the bearer token to `~/.amc/cli-token` with hardened permissions (Unix `0o600`, Windows ACL grant). The recipe routes are registered by `registerRecipeRoutes()` in [/src/main/services/cli/cli-server-recipe-routes.ts](/src/main/services/cli/cli-server-recipe-routes.ts) (wired from `register-cli-routes.ts` at startup):

- `GET /recipe/patterns` and `GET /recipe/patterns/:id` — read-only, list bundled patterns from `resources/recipe-patterns/`
- `POST /recipe/draft` — translates NL to a `RecipeConfig` via `RecipeDrafterService` (mirrors the `RECIPE_CREATE_FROM_NL` IPC); rate-limited
- `POST /recipe/validate` — runs `runSemanticChecks` + `runWarningChecks` + `estimateRun`; rate-limited
- `GET /recipe/configs` and `GET /recipe/configs/:id` — read-only, list/find saved recipes (global + per-project, deduplicated)
- `POST /recipe/save` — persists via `recipeFileStore.saveRecipeConfig`; **forces `approvalStatus: 'pending'` server-side** regardless of body; emits `RECIPE_AUTHORING_APPROVAL_NEEDED` push so the renderer's inbox refreshes, plus `RECIPE_CONFIG_CHANGED` (RT-F005) so open Recipes lists live-refresh; returns 201
- `PUT /recipe/configs/:id` — looks up the existing recipe, normalizes both sides through `recipeSaveConfigSchema`, compares behavioral fields (`steps`, `pipelines`, `parameters`, `supervisors`, `limits`) via `stableStringify`; resets approval to `pending` only when behavioral fields changed AND the current state is `approved` or `rejected`; emits the approval push when reset fires, and always emits `RECIPE_CONFIG_CHANGED` (RT-F005) so open lists refresh; returns 200

The approval state machine is `'not_required' | 'pending' | 'approved' | 'rejected'` (with `undefined` treated as legacy back-compat). The runtime gate lives in three places, all routing through the shared `checkRecipeApprovalStatus` helper in [/src/main/services/recipe/recipe-validation.ts](/src/main/services/recipe/recipe-validation.ts) so the contract — pending / rejected refuse, approved / not_required / undefined proceed — cannot drift between surfaces: (1) the `RECIPE_START` IPC handler in [/src/main/ipc/recipe-handlers.ts](/src/main/ipc/recipe-handlers.ts) (manual run from the Recipes UI), (2) the tick loop in [/src/main/services/recipe/recipe-scheduler-service.ts](/src/main/services/recipe/recipe-scheduler-service.ts) (filters non-approved recipes out before scheduling), and (3) `handleRecipeTriggerViaCli` in [/src/main/ipc/recipe-handlers.ts](/src/main/ipc/recipe-handlers.ts) (the agent-trigger CLI path that backs `POST /recipes/run`). The user's approve/reject decisions flow back through the `RECIPE_AUTHORING_RESPOND` IPC handler in [/src/main/ipc/recipe-authoring-handlers.ts](/src/main/ipc/recipe-authoring-handlers.ts), which writes the new `approvalStatus` to disk via `recipeFileStore.saveRecipeConfig` and emits a `RECIPE_CONFIG_CHANGED` push (RT-F005) so any other open Omniscio window's recipe list stays in sync.

The `RecipeConfig.approvalStatus` field is defined in [/src/shared/types.ts](/src/shared/types.ts) and validated by `recipeSaveConfigSchema` in [/src/shared/ipc-schemas.ts](/src/shared/ipc-schemas.ts). The skill that external AIs follow is the `omniscio-control` bundle — its [recipes surface doc](/.claude/skills/omniscio-control/recipes.md) walks the full authoring flow (clarify → check patterns → draft → validate → save → iterate) end-to-end.

## Related

- [use-recipes.md](use-recipes.md) — what a `RecipeConfig` looks like and how runtime execution works
- [use-recipe-drafter.md](use-recipe-drafter.md) — the in-app NL drafter (RECIPE_CREATE_FROM_NL IPC) that Omniscio's own UI uses
- [cli-control.md](cli-control.md) — the CLI control server in general, all endpoints
- [create-cron-job-with-ai.md](create-cron-job-with-ai.md) — the same authoring pattern, applied to cron jobs
- [automations-and-auto-replies.md](automations-and-auto-replies.md) — the same authoring pattern, applied to automation rules
- [.claude/skills/omniscio-control/recipes.md](/.claude/skills/omniscio-control/recipes.md) — the skill external AIs follow when authoring on your behalf
