dystopic docs is in beta — content is actively being added.
dystopic
Reference

CLI reference

The command surface for the whole flow — auth, config, whoami/health, odyssey init/push/publish/dev, the platform-authoring groups (scenarios, suites, agents gate/ci-suite, repo), ci init/review --platform-suite, runs export, and the credential/base-url resolution precedence.

The SDK ships two console scripts. dystopic is the engine; odyssey is the agent namespace on top of it. Everything in the flow — logging in, registering an agent, running checks, and gating a PR — is reachable through them.

pip install 'dystopic[odyssey]'

odyssey <command> is exactly dystopic odyssey <command>: the odyssey script and the dystopic odyssey … prefix dispatch to the same code. The flow uses the odyssey spelling throughout; use whichever you prefer.

The two scripts

dystopic

The engine and console entry point. Owns global flags, credential resolution, and output formatting. Exposes auth, config, whoami, health, version, logout, the top-level ci command, and the platform-authoring groups — scenarios, suites, agents gate / agents ci-suite, and repo — directly.

odyssey

The agent namespace: init, push, publish, runs, list, show, versions, ci, plus the authoring commands (doctor, scaffold, mcp, sync, dump-agent) that require the [odyssey] extra.

Run odyssey <command> --help for a command's options, or dystopic <command> --help for the engine commands.

Global flags

These flags are understood by the engine. --base-url, --verbose, --quiet, and --no-dotenv are position-independent — they may appear before or after the subcommand (dystopic auth login --base-url https://api.pipelines.tech is normalized to put --base-url first). The rest must precede the subcommand.

FlagEffect
--base-url URLTarget a different API host for this invocation. The only way to retarget — there is no named-environment alias.
--api-key KEYSupply the API key for this invocation instead of resolving one.
--output {json,yaml,table}, -oOutput format. Unset renders a compact human summary for read/status commands and raw JSON for everything else; -o json always prints the raw object.
--quietPrint only the final resource ID (e.g. the check_id).
--verboseEnable debug logging — logs each request to stderr.
--no-dotenvDo not auto-load a .env file from the current directory.
--versionPrint the SDK version and exit.

By default the CLI loads a .env from the current directory (if python-dotenv is installed, which the [odyssey] extra pulls in); shell environment variables always win over it. Pass --no-dotenv to skip that autoload.

Credential & base-url resolution

Every command that talks to the API resolves an API key and a base URL through the same precedence chain. The single source of truth — including how auth login stores the key in your OS keychain — lives in Set up the CLI. In short:

  • API key: --api-keyDYSTOPIC_API_KEY › OS keychain / config file (written by auth login).
  • Base URL: --base-urlDYSTOPIC_BASE_URL (or DYSTOPIC_INTERNAL_BASE_URL) › the base_url saved in config › https://api.pipelines.tech.

.env values slot in below flags/shell env but above the config file (they populate the environment before resolution runs). When no key resolves, the command exits 2 with:

Missing DYSTOPIC_API_KEY. Set it as an environment variable, CLI flag, or run `dystopic auth login`.

Environment variables

VariablePurpose
DYSTOPIC_API_KEYAPI key, used when no --api-key flag is given.
DYSTOPIC_BASE_URLOverride the API base URL (also DYSTOPIC_INTERNAL_BASE_URL).
DYSTOPIC_CONFIG_FILEOverride the config file path (default ~/.config/dystopic/config.json).

Auth

Browser login is the default. With no --api-key, auth login opens your browser to sign in and mints a key; pass --no-browser to print the verification URL and poll instead, or --api-key KEY to save a key directly. If you already have a valid saved key, login reuses it — pass --force to mint a new one.

# Browser sign-in (default) — saves the key for future CLI/SDK calls.
dystopic auth login

# Headless: print the URL to visit and poll.
dystopic auth login --no-browser

# Save a key you already hold.
dystopic auth login --api-key pk_live_...

# Confirm the saved key works and see which host it points at.
dystopic auth status

auth status prints a ✓ Authenticated to <base-url> (or ✗ Not authenticated) line.

# Remove the saved API key (clears the whole config file).
dystopic logout

# Keep base_url/org_id/email; only drop the api_key.
dystopic logout --keep-base-url

Config

Inspect or edit the on-disk SDK config (~/.config/dystopic/config.json unless DYSTOPIC_CONFIG_FILE overrides it). config show masks the API key and notes whether it lives in the OS keychain or plaintext in the file.

dystopic config show          # print the config (api_key masked)
dystopic config path          # print the resolved config file path
dystopic config set base_url https://api.pipelines.tech
dystopic config unset base_url
dystopic config clear         # delete the config file entirely

Recognized keys include base_url, api_key, and email.

odyssey — agent lifecycle

Register an agent with the web dashboard or the SDK (create_code_agent(...)) — that is the primary path and it needs no local manifest. The odyssey init + odyssey push commands below are a secondary, manifest-driven alternative for teams who prefer to keep a checked-in registration file. See Register your agent for the full walkthrough and the manifest schema.

The lifecycle read/write commands (init, push, publish, runs, list, show, versions, ci) run on the base [cli] extra. The authoring commands further down require [odyssey].

odyssey init

Interactive wizard that scaffolds a minimal agent-registration manifest (dystopic.yaml) in the current directory — agent name, mode, and entrypoint/source only. Fully offline; no API key required. This is the entry point for the secondary, manifest-driven registration path; most teams register on the dashboard or via the SDK instead.

odyssey init

See Register your agent for the manifest fields it writes.

odyssey push

Create or update the agent on the platform from the manifest. Requires an API key. This is the secondary registration path — the primary is the dashboard or SDK create_code_agent(...).

odyssey push                    # push the only agent (or name it)
odyssey push my-agent           # push a specific agent by name
odyssey push --all              # push every agent in the manifest
odyssey push my-agent --publish # push, then activate the draft
FlagEffect
name (positional)Agent name from the manifest (default: the only one).
--allPush every agent in the manifest.
--publishActivate the agent immediately after pushing the draft.
--set-credential TYPE=VALUESet an org credential value (repeatable; also read from os.environ[TYPE]).

The relocated dystopic agents push spelling is the same command; it prints a redirect to odyssey push (see Relocated legacy commands).

odyssey publish

Activate a pushed draft agent.

odyssey publish my-agent   # by manifest name
odyssey publish --id 42    # by numeric agent id (skips the manifest)

odyssey list / odyssey show

odyssey list                    # agents you can run against (NAME / ID / MODE / STATUS)
odyssey list --search billing
odyssey show 42                 # one agent by id

odyssey versions

List, inspect, or diff an agent's version snapshots.

odyssey versions --agent-id 42
odyssey versions --agent-id 42 --version 3
odyssey versions --agent-id 42 --from-version 2 --to-version 3
FlagEffect
--agent-id IDAgent row to inspect (required).
--version NReturn one version instead of the full newest-first history.
--from-version NCompare against --to-version (or latest if omitted); renders a readable diff.
--to-version NThe version to compare --from-version against (defaults to latest).
--jsonEmit the diff as JSON instead of the readable summary.
--color {auto,always,never}Colorize readable diffs (default auto).

Platform authoring — scenarios, suites, gate, CI binding

Suites, scenarios, the gate, and the CI suite binding are platform objects on your agent. These dystopic command groups are typed wrappers over the agent-scoped REST endpoints (.../scenarios, .../suites, .../gate, .../ci-suite) — the same endpoints the dashboard drives. The golden-path walkthrough is 4 · Create a suite; the request bodies live in the create-suite reference.

dystopic scenarios

Scenario CRUD plus CSV bulk-import. list/show print the result; the mutating commands print progress to stderr and exit 0/1.

dystopic scenarios list 42 [--group refunds] [--limit 50] [--offset 0]
dystopic scenarios show 42 101
dystopic scenarios create 42 --name refund-simple \
  --user-instruction "Refund order #1234." --expected-outcome completion
dystopic scenarios update 42 101 --expected-outcome refusal
dystopic scenarios delete 42 101
dystopic scenarios import 42 seeds.csv --suite-id 7 --append
Flag (create/update)Effect
--name NAMEStable human-readable id (required on create).
--user-instruction TEXTThe task text handed to the agent (a content axis).
--expected-outcome {completion,refusal}The outcome the reviewer judges against.
--behavior-instructions TEXTDirections to the world/user simulator (a content axis).
--failure-rules RULEA failure rule; repeat the flag to build a list.
--group NAMEBucket label (scenario_group).
--world-id IDPin the world this scenario runs in.

JSON-shaped fields (initial_state, scorers, and the multi-turn conversation object) are set from the dashboard or the SDK (client.create_scenario(...)), not CLI flags.

import parses the CSV client-side and creates one scenario per row (same column dialect as the dashboard importer); rows with no content field are skipped, and a non-numeric max_turns cell is dropped rather than aborting the import. With --suite-id, the imported scenarios are bound to that suite — an atomic full-replace of the suite's bindings by default; --append keeps the existing bindings and adds the imported scenarios after them (deduped).

dystopic suites

Suite CRUD plus the suite ↔ scenario binding.

dystopic suites list 42
dystopic suites show 42 7
dystopic suites create 42 --name default [--description D] [--world-id W]
dystopic suites update 42 7 --description "PR regression set"
dystopic suites delete 42 7
dystopic suites scenarios 42 7          # the bound scenarios, in run order
dystopic suites bind 42 7 101 102 103   # ATOMIC full-replace; arg order = run order
dystopic suites unbind 42 7 101         # drop one binding, scenario untouched

bind is not an append: scenarios not listed are unbound.

dystopic agents gate

The agent's platform gate — the rule that turns a base-vs-head diff into pass or block. It is platform-authoritative: RBAC'd, audited, and not weakenable from a PR.

dystopic agents gate get 42
dystopic agents gate set 42 --block-on-constraint \
  --reviewer-severity critical --min-pass-rate 0.8 --scorer policy_check
dystopic agents gate clear 42
dystopic agents gate import 42 dystopic.yaml [--suite NAME]   # legacy-yaml migration
Flag (gate set)Tier
--block-on-constraintBlock when a tracked constraint regresses base→head.
--reviewer-severity {nit,warning,critical}Block at or above this LLM-reviewer severity.
--min-pass-rate RATEBlock when the pass rate falls below RATE (0..1).
--scorer NAMEBlock when scorer NAME regresses (repeatable).

set with no tier flags persists an all-empty advisory gate — still a configured gate; clear (the DELETE) is the only way to un-configure it. gate import is the one-shot migration for teams with a legacy committed config: it lifts a suite's block_on list out of the file and persists it as the platform gate.

dystopic agents ci-suite

Bind (or clear) the suite CI resolves for this agent — what makes ci review --platform-suite source that suite's frozen scenarios server-side.

dystopic agents ci-suite set 42 7     # suite must belong to agent 42
dystopic agents ci-suite clear 42

dystopic repo

Inspect or create the repo ↔ platform connection CI depends on. status and connect print to stderr and exit 0/1 (status is scriptable as a connected/not-connected predicate).

dystopic repo list                      # repos connected to your org
dystopic repo status owner/name         # exit 0 connected / 1 not
dystopic repo connect owner/name [--agent-id 42]

connect claims the repo headlessly — no browser bounce — provided your org has already installed the GitHub App and added this repo to it. --agent-id links a specific org-owned umbrella agent; omitted, the platform auto-provisions one. The first-time App install is inherently browser-bound (GitHub has no headless install API): when the repo isn't claimable yet, connect prints the install URL (or the list of repos that are claimable) and exits 1. Already-connected repos are a no-op exit 0. See 7 · Integrate into CI/CD.

odyssey runs

Read-side commands for past checks.

odyssey runs list                        # recent checks (RUN / STATUS / PASSED/TOTAL)
odyssey runs list --agent my-agent
odyssey runs show 512                     # one check, rendered
odyssey runs export 512 --json out.json --junit out.xml

odyssey runs export CHECK_ID writes the full check rollup (every case, paged internally) to the paths you give:

FlagEffect
check_id (positional)The check id to export.
--json FILEWrite the full rollup as JSON.
--junit FILEWrite a JUnit XML report.

With neither flag, it prints a note reminding you to pass --json and/or --junit. It warns to stderr before overwriting an existing file.

ci — the PR check

The CI surface drives the branded Dystopic merge check. It is exposed both as the top-level dystopic ci … and, identically, as odyssey ci …. Its two core subcommands are init (scaffold the workflow) and review (the PR gate); cancel, dismiss, and rerun manage an existing review.

dystopic ci init

Scaffold the PR-check workflow into the current repo — offline, no API key. Platform-first by default: it writes exactly one file, the .github/workflows/pipelines-regression.yml workflow in platform-suite (zero-yaml) mode, and no local config — a connected repo needs nothing else, because CI resolves the run recipe, gate, and bound suite from the platform. It then prints the turn-it-on checklist: dystopic repo connect, author scenarios/a suite (dystopic scenarios import …), and dystopic agents ci-suite set.

dystopic ci init
dystopic ci init --force    # overwrite scaffold files that already exist
dystopic ci init --legacy   # old three-file scaffold (dystopic.yaml + seeds.csv + workflow)

The scaffolded workflow sets the action's platform-suite: true input, which requires an action release that includes it — re-release @v1 (or pin a revision that has it) if your org's action copy predates SDK 0.5.0. --legacy restores the old committed-config skeleton for the pre-connected-repo path; the recommended front door is to connect your repo on the platform. See CI/CD for the connect-repo walkthrough.

dystopic ci review

Submit the base and head sides as commit-keyed checks and gate on the assembled review. This is what the workflow runs on each PR. odyssey ci review is the same command.

# PR mode: gate head against the merge-base, zero committed config.
dystopic ci review --platform-suite \
  --base "$MERGE_BASE_SHA" --head HEAD --repo owner/name --pr-number 42

# Push / non-PR mode: head-only, gated on the absolute (floor) gate.
dystopic ci review --platform-suite --head-only --branch main

--platform-suite is the zero-yaml mode and the recommended default. The side submits no local config and no scenarios: the platform resolves the execution recipe from the connected repo's umbrella agent and sources the bound suite's frozen scenarios server-side, byte-identical to an in-app check. It fails closed (exit 1 / 422) if the repo is unconnected, the umbrella agent has no run command, or the suite doesn't resolve. The gate lives on the platform and cannot be weakened or removed by a PR — dropping config on head does not disarm the check.

FlagEffect
--platform-suiteZero-yaml mode (recommended). Submit no local config or scenarios; the platform resolves the recipe from the umbrella agent and sources the bound suite's frozen scenarios server-side.
--suite NAMESuite selector (default default).
--all-suitesReview every configured suite over the same two worktrees; the exit code is the worst across suites.
--base SHABase git sha/ref — the PR's merge-base (required unless --head-only).
--head REFHead git ref/sha (default HEAD).
--repo owner/nameRepository (default $GITHUB_REPOSITORY).
--pr-number NPR number (default $PR_NUMBER). Mutually exclusive with --head-only.
--head-onlySubmit only the head side and gate on the absolute floor gate; needs --branch (or $GITHUB_REF_NAME).
--branch NAMEBranch name for --head-only reviews (default $GITHUB_REF_NAME).
--sample NOnly run the first N scenarios.
--repeats NRepeats per scenario (default 1).
--freshForce fresh executions instead of attaching to existing commit-keyed runs — costs real runs.
--baseline-review IDClient-side drift compare against a prior assembled review (requires --head-only and --fresh).
--jsonEmit the machine dump to stdout (the composite Action consumes it).
--poll-interval SECONDSPoll cadence (default 5.0).
--timeout SECONDSPoll budget in seconds (default 1200.0); exhaustion is not a failure — the platform finishes asynchronously.

Exit codes:

CodeMeaning
0Gate pass / neutral / advisory / skip-neutral, or the poll budget was exhausted (the platform finishes the review async).
1Gate blocked; a gated-red preflight (config or gate removed vs. base); or an operational HTTP failure mid-flow.
2Usage error, or a named preflight error with nothing submitted (e.g. an unconnected repo, an umbrella agent with no run command, or a suite that doesn't resolve).

Use dystopic ci review / odyssey ci review (the v2 surface, backed by /api/ci/reviews). This is the only supported CI review command.

See the CI/CD reference for the full decision table, preflight error names, and workflow wiring.

odyssey authoring commands

These require the [odyssey] extra; running one without it prints an install hint and exits 2.

The authoring commands: odyssey scaffold (scaffold a runnable agent project), odyssey doctor (local green/red preflight), odyssey mcp (introspect MCP servers into a tools_schema), odyssey sync (sync a wrapper's tools_schema to the platform), and odyssey dump-agent (print the JSON registration payload).

Diagnostics

dystopic whoami     # the user behind the current API key (email / name / org / role)
dystopic health     # API health, ✓/✗ per dependency
dystopic version    # SDK version

dystopic --version prints the same version and exits.

Relocated legacy commands

Agent lifecycle commands moved under the odyssey namespace. Typing a relocated top-level command directly at the console — dystopic init, dystopic agents push, or dystopic runs … — prints a one-line redirect to the odyssey spelling on stderr and exits 2. The zero-yaml authoring commands are the exception: dystopic agents gate … and dystopic agents ci-suite … (like dystopic scenarios/suites/repo) did not relocate and run directly.

`dystopic agents push` has moved — agent commands now live under `odyssey`.
  Run:  odyssey push
  (or:  dystopic odyssey push)

Use the odyssey spelling (or the explicit dystopic odyssey … prefix) instead.

Next

With the command surface in hand, walk the flow end to end starting at Port your agent, or jump to CI/CD to wire up the Dystopic check.