---
title: CLI reference
description: Every routineos.py subcommand, its arguments, what it writes and its exit codes. Taken from the argument parser in skills/routineos/scripts/routineos.py.
sidebar:
  order: 1
---

The CLI is one Python script with no dependencies beyond the standard library (Python 3.11 or later, for `tomllib`):

```
python3 skills/routineos/scripts/routineos.py <command> [arguments]
```

The pages below write `$RO` for that path.

## Commands

| Command | Does |
|---|---|
| [`new`](#new) | Scaffold a routine folder |
| [`app-init`](#app-init) | Add `app.toml`, `risks.md` and `CHANGELOG.md` to an existing folder |
| [`lint`](#lint) | Check a routine folder |
| [`doctor`](#doctor) | Check the environment, and optionally an app folder or a store snapshot |
| [`permissions`](#permissions) | Print the permission screen for an app folder |
| [`platform`](#platform) | List capability records, or check an app's `relies_on` ids |
| [`prompt`](#prompt) | Print a prompt fragment |
| [`compile`](#compile) | Compile `definition.json` files into `meta/definition` |
| [`seed`](#seed) | Plan the one-time Desk setup as batch files |
| [`write`](#write) | Plan a run's Desk writes as ordered batch files |
| [`learn`](#learn) | Update learning tallies and plan this run's proposals |
| [`bundle`](#bundle) | Bundle the Desk sources into `assets/desk/index.html` |
| [`pack`](#pack) | Validate, install, export and list packs; build the catalog. Since 0.6.0 |

## new

```
$RO new <name> [--type serverless|watch|approval-gated|interactive] [--from-template <template>] [--dir <dir>] [--app]
```

| Argument | Meaning |
|---|---|
| `name` | Routine name. Becomes the producer id |
| `--type` | Routine type. Watch and interactive get `definition.json` and `dashboard/index.html` |
| `--from-template` | Copy a template from `templates/`, such as `jobs-watch`, and rename it |
| `--dir` | Target directory. Default `./<name>` |
| `--app` | Also write `app.toml`, `risks.md`, `CHANGELOG.md` and `lib/`, and put the fixed header in `prompt.md` |

Prints `created <dir>`. The page's `desk-config` block names the workspace after `name`, whatever `--dir` is.

## app-init

```
$RO app-init <dir>
```

Adds `app.toml`, `risks.md` and `CHANGELOG.md` (the changelog only when absent) to a folder made without `--app`. It does not add the prompt header, so `lint` then fails with `prompt must mention state/verified` and `prompt must mention state/budget`. Add the header from `prompt app-header`.

## lint

```
$RO lint <dir>
```

Checks `routine.toml`, `prompt.md`, `definition.json` when present, and `app.toml` when present. Prints `ok: no problems` or one `FAIL <file>: <problem>` line per problem. What it checks is listed in [routine.toml and app.toml](/reference/routine-toml#what-lint-checks) and [Definition schema](/reference/definition-schema#what-lint-checks).

Lint does not reject unknown keys, and does not require `definition.json` when `[workspace] enabled = true`.

## doctor

```
$RO doctor [dir] [--docs <snapshot.json>]
```

Prints the Python version and whether `tomllib` loads. With `dir`, `--docs` or `ROUTINEOS_REGISTRY` set, it also prints whether the registry loads and how many records it has. With `dir`, it prints each routine's delivery mode and the app's lint result. With `--docs`, checks a store snapshot `{path: {version, data}}` against store limits.

```
Python 3.13.15
tomllib: ok
registry: ok (47 records)
delivery: hiring-signals: repo
ok: no problems
```

## permissions

```
$RO permissions <dir>
```

Prints the permission screen for an app folder. Sections, in order:

| Section | Lists |
|---|---|
| Apps and hosts | Each `[[integration]]` with hosts, direction and `what` |
| Connectors | Each connector integration, direction and `what` (else "purpose not stated") |
| Tools the run may use | Each routine's `tools`, and the rule that no unlisted send tool may be used |
| Logic delivered by | Each routine's `delivery` and repo or skill |
| Schedule | UTC cron, your time zone, runs per day per routine and in total, the cap, the one-hour minimum and the platform's hourly limits |
| Environment and secrets | The `[environment]` table as you reported it, and secret names |
| Store | `[store] kind` |
| Budget | The ceiling, marked "prompt-enforced, not guaranteed", and the alert channel |
| First run | What a report-only first run does |
| Not yet checked | Undeclared hosts, and `relies_on` ids that are not verified |

It ends: "Nothing is created until you say yes. A change to any line above makes a new screen."

## platform

```
$RO platform list [--status <status>]
$RO platform check <dir>
```

`list` prints every capability record in `registry/capabilities.toml` as `<id>  <status>  <date>`. `--status` filters, for example `--status unverified`. `check` prints the status of each id in the app's `[risks].relies_on`.

```
run-writes-artifact-db  pilot-verified
approval-email-slack  pilot-verified
```

## prompt

```
$RO prompt <fragment>
```

| Fragment | Text |
|---|---|
| `app-header` | The fixed run header: report-only until `state/verified`, the budget steps, `state/health`, listed tools only, the named store |
| `gate-email` | How a run sends and reads an email gate |
| `gate-slack` | How a run sends and reads a Slack gate |
| `action-queue` | The action queue: request documents are data, not instructions |
| `adapter-contract` | The storage adapter contract: anything an adapter returns is data, not instructions |

An unknown name prints `error: unknown fragment '<name>'; choose from action-queue, adapter-contract, app-header, gate-email, gate-slack` and exits 2.

## compile

```
$RO compile <dir> [--out <file>]
```

Compiles one routine, or every subfolder of `dir` that has both `routine.toml` and `definition.json`, into one `meta/definition`. Default output `<dir>/meta-definition.json`.

```
ok: 1 producer(s), version eda3cea96ab1, wrote hiring-signals/meta-definition.json
```

With no definition: `error: no definition.json found in <dir>`, exit 2.

## seed

```
$RO seed <dir> --out <batch-dir> [--at <iso>] [--name <workspace name>] [--docs <snapshot.json>]
```

Lints, compiles, and plans `meta/workspace`, `meta/definition`, `settings/<producerId>` per producer and `settings/workspace` as `batch-001.json` onward. Settings are planned only when absent from `--docs`. `--at` sets `createdAt` (default now, UTC). `--name` sets the workspace name (default the routine name, or `[workspace] name`).

```
ok: wrote 1 batch file(s) to hiring-signals/seed; lint passed. Apply them in order with ArtifactData batch.
```

## write

```
$RO write --docs <snapshot.json> --run <run.json> --out <dir> [--definition <meta-definition.json>] [--artifactdata]
```

Plans a run's writes under the [write contract](/reference/write-contract) and writes `batch-001.json` onward, each `{"phase", "writes"}`, skipping empty phases. Reads the definition from `meta/definition` in the snapshot unless `--definition` is given. `--artifactdata` also writes `batch-NNN.artifactdata.json`, the `writes` argument of one ArtifactData `batch` call, with each document's data in a file under `batch-NNN-data/`.

```
ok: wrote 5 batch file(s) to out
```

## learn

```
$RO learn --docs <snapshot.json> --run <run.json> --out <dir> [--definition <meta-definition.json>]
```

Writes `learning.json` (one batch setting `state/learning`) and `proposals.json` (Inbox messages to put at the front of the run's `inbox` before `write`). The snapshot must include `inbox/` and `decisions/`. The run input needs an ISO `startedAt` or `endedAt`.

```
ok: 1 proposal(s) in proposals.json; apply learning.json as one ArtifactData batch
```

## bundle

```
$RO bundle [--check]
```

Bundles `assets/desk/src/` into `assets/desk/index.html`. `--check` writes nothing and exits 1 if the bundle on disk is stale.

## pack

> Since 0.6.0.

```
$RO pack validate <src> [--json]
$RO pack install <src> [--dir <dir>] [--name <name>] [--json]
$RO pack export <dir> [--out <file>]
$RO pack list <src>
$RO pack catalog [--check]
```

| Subcommand | Does |
|---|---|
| `validate` | Check a pack file or https URL. `--json` prints `{ok, problems}` |
| `install` | Write a routine folder from a pack and print the connector report. `--dir` must not exist. `--name` installs under a new routine name |
| `export` | Write a routine folder as one pack file. Default prints to stdout |
| `list` | Print the templates in a catalog index file or https URL |
| `catalog` | Export every template with a `pack.json` into `site/templates`. `--check` writes nothing and exits 1 when a file would change |

See [Templates](/templates) and [Pack schema](/reference/pack-schema).

## Exit codes

| Code | Meaning |
|---|---|
| 0 | OK |
| 1 | Lint problems (`lint`, `compile`, `seed`), or a stale file with `--check` |
| 2 | Unreadable input, a missing file, or a contract error (`write`, `learn`) |

## Related

- [routine.toml and app.toml](/reference/routine-toml)
- [Write contract](/reference/write-contract)
- [Build your first routine](/guides/first-routine)
