Skip to main content

sufleur-cli

The CLI for Sufleur — the registry where you author, version, and publish LLM prompts. This is the consumer side: it installs prompts from your Sufleur workspace into your project the way pip installs packages — declared in sufleur.yaml, locked to sufleur-lock.yaml, generated into one Python module with full types and runtime helpers.

Create a workspace and start authoring prompts at https://sufleur.com.

What you call from your code

from generated.prompts import get_prompt

review = get_prompt("@my-workspace/code-review")

rendered = review.render("en", {"diff": "...", "language": "go"})
prompt: str = rendered["prompt"]  # ready-to-send prompt string

result = review.parse_output(llm_response_text)
if result["success"]:
    result["data"]  # Pydantic model, validated against the prompt's output schema
else:
    result["error"]

"@my-workspace/code-review" is checked at type-check time (mypy / pyright): typos fail, the entrypoint name "en" is narrowed against the prompt's available entrypoints (via @overload), and the input is a TypedDict derived from the JSON Schema declared on that entrypoint. The version that resolves at codegen time is pinned in sufleur-lock.yaml.

Install

pip install sufleur-cli
sufleur --help

Or with pipx for an isolated install:

pipx install sufleur-cli

The wrapper ships the prebuilt binary inside a per-platform wheel — pip selects the right one via PEP 425 platform tags. There's no Python interpreter in the invocation hot path; sufleur is the native binary on your PATH.

Quick start

mkdir my-app && cd my-app
sufleur init                                  # creates sufleur.yaml interactively
sufleur add @my-workspace/code-review ^1.0.0  # add + fetch + lock
sufleur generate                              # writes ./generated/prompts.py

The generated module imports two runtime peers. Install them with the [generated] extra:

pip install 'sufleur-cli[generated]'

…or add chevron (Mustache templating) and pydantic (output-schema validation, only needed when prompts have output schemas) directly to your project's dependencies. The CLI itself has no Python runtime deps; the [generated] extra exists so users who run init/add/install but never generate aren't forced to install code they don't use.

The generated code targets Python 3.10+ (PEP 604 union syntax).

What sufleur generate emits

A single .py module containing every prompt inlined (no runtime fetches). The public API is get_prompt(name), which returns a result object with:

  • render(entrypoint, input) → {"prompt": str} — Chevron renders the entrypoint template against input. The signature is narrowed via @overload per entrypoint, so type checkers reject the wrong input shape.
  • metadata — a TypedDict containing version, your workspace's custom metadata, and (when applicable) outputSchema.
  • parse_output(raw) (only present if the prompt has an output schema) — strips ``` fences, JSON-parses, and validates with a Pydantic model generated from the prompt's JSON Schema. Returns {"success": True, "data": <Model>} or `{"success": False, "error": str}`.

Plus generated TypedDicts per entrypoint, with field docstrings for any schema property that has a description:

class CodeReview_EnInput(TypedDict):
    diff: str
    """The unified diff to review."""
    language: str

Optional schema properties are wrapped in typing.NotRequired[...], and oneOf schemas become PEP 604 unions (X | Y).

Prompts published with DRAFT status emit a warnings.warn(...) when their get_prompt is called.

Decision prompts (TypeSafe Jev)

Prompts for System-One decision models such as TypeSafe's Jev are question templates: typed questions (noul, choice, score) your code asks about a state it builds. They're emitted as get_decision(name), which returns an object with:

  • batch() — ask(question_id, inputs, key=..., options=...) renders a question and returns a typed DecisionHandle (the key defaults to the question id; ask a question more than once under distinct keys). items() lists (key, question_id, question) to send. read(answers) validates each answer against its question (get(handle) → AnswerResult, get_or_throw(handle) → the answer model); read_all(answers) fails unless every answer is valid.
  • question(question_id, inputs, options=...) and parse_answer(question_id, raw) — render or validate a single question.
  • metadata — version and the recommended modelConfig, as for get_prompt.
triage = get_decision("@my-workspace/ticket-triage")
batch = triage.batch()
team = batch.ask("department", {"product": "Acme"})

res = httpx.post(
    "https://api.typesafe.ai/v1/systemone",
    headers={"Authorization": f"Bearer {os.environ['TYPESAFE_API_KEY']}"},
    json={
        "model": triage.metadata["modelConfig"]["model"],
        "state": {"ticket": ticket},
        "questions": {key: question for key, _, question in batch.items()},
    },
).json()

batch.read(res["answers"]).get_or_throw(team).choice  # "billing" | "technical" | "sales"

The generated code has no provider request or response shape: you make the call (TypeSafe, OpenRouter's decisions endpoint, …) and add no runtime dependencies beyond chevron and pydantic. Guide: https://sufleur.com/docs/decision-models.

sufleur.yaml

The manifest. Looks like:

api_keys:
  my-workspace: ${MY_WORKSPACE_API_KEY}

prompts:
  '@my-workspace/greeting': '*'
  '@my-workspace/code-review': '^2.0.0'
  # alias: keep two pinned versions side-by-side under different names
  '@my-workspace/code-review-strict': '@my-workspace/code-review@~1.4.0'

output:
  language: python
  file: ./generated/prompts.py

api_keys is optional: public prompts install without any key (like public npm packages). A key is only needed for the private prompts of a workspace.

Constraints are npm-style semver ranges (^, ~, >=, exact, *). The resolution is recorded in sufleur-lock.yaml. Commit both files — sufleur.yaml is the source of truth, sufleur-lock.yaml is the receipt.

CI usage

sufleur install --frozen   # fail if lockfile is stale
sufleur generate

--frozen is the pip-compile-equivalent: refuses to update the lockfile, hard-errors if the manifest and lockfile disagree.

Commands

Command Description
sufleur init Interactive scaffolding for sufleur.yaml.
sufleur add @ws/name [range] Add a prompt, fetch it, update the lockfile. --alias <name> keeps multiple versions; --force overwrites an existing entry.
sufleur remove @ws/name Remove a prompt from the manifest and prune its cache (kept if another alias still resolves to the same version).
sufleur install Resolve the manifest, fetch what's missing, refresh the lockfile. --frozen for CI.
sufleur update [@ws/name] Re-resolve constraints — one prompt or all.
sufleur generate Regenerate the output file from the lockfile + cache.

-v / --verbose enables HTTP request/response logs on any command. Variables in .env are loaded automatically; per-workspace API keys can be referenced as ${ENV_VAR_NAME} in sufleur.yaml.

Authoring prompts from the CLI

The commands above install published prompts into your project. The CLI also exposes the full authoring side — designed so a coding agent (Claude Code, Cursor, etc.) can create, version, and edit prompts in your Sufleur workspace on your behalf.

Hand it to your agent

sufleur skill prints a markdown skill description — when to use the CLI, FQ-name format, the full command surface, JSON flags. Pipe it wherever your agent loads skills from:

# Claude Code (each skill is a directory with a SKILL.md inside)
mkdir -p ~/.claude/skills/sufleur && sufleur skill > ~/.claude/skills/sufleur/SKILL.md

# Cursor
sufleur skill > .cursor/rules/sufleur.md

The skill ships inside the binary, so it always matches the sufleur version on your PATH.

Log in

sufleur login    # device-code flow — opens a browser, polls until approved
sufleur me       # show the authenticated user
sufleur logout   # revoke the stored credential

Credentials land in $XDG_CONFIG_HOME/sufleur/credentials.yaml (or ~/.config/sufleur/credentials.yaml). This user credential is separate from the workspace API keys referenced in sufleur.yaml — those stay machine-to-machine, this one identifies you as the author.

Authoring commands

All accept --json. Prompts are addressed as @workspace/name, versions as @workspace/name@version (use the literal label draft while the version is unpublished).

Command What it does
workspace list List the workspaces you belong to, with your role
prompt create @ws/name --description "..." Create a new prompt in a workspace
prompt create @ws/name --kind system-one Create a decision-model prompt (TypeSafe Jev); the kind is fixed
prompt list @ws [--search ... --limit ... --offset ...] List prompts in a workspace
prompt get @ws/name Show one prompt's details
prompt update @ws/name --description "..." Update the description
version draft @ws/name Fork the latest published version into a new draft
version list @ws/name [--status DRAFT|PUBLISHED] List versions of a prompt
version get @ws/name@version Show one version's details
version delete @ws/name@draft Delete a draft (published versions are immutable)
version set-metadata @ws/name@draft --string K=V (or --from-file …) Patch or sync metadata
version delete-metadata @ws/name@draft --key K Remove a metadata key
version set-output-schema @ws/name@draft --file schema.json Replace the version's output schema
version set-decision-spec @ws/name@draft --from-file decision.yaml Set a decision prompt's question templates (types, criteria, optionCriteria)
version set-model-config @ws/name@draft --provider anthropic --model NAME [--params '{...}'] Set the version's provider/model/parameters
version set-readme @ws/name@draft [--content STR | --file PATH] Replace the version's README
version get-readme @ws/name@version Print the version's README to stdout (raw markdown)
version dump @ws/name@version --to ./dir Export files, output schema, README, and metadata to disk
file list @ws/name@version List files in a version
file create @ws/name@draft --file path.mustache [--entrypoint] [--format text|yaml] Add a new file (.yaml.mustache implies --format yaml)
file update @ws/name@draft --name X [--file ...] [--rename Y] Replace content and/or rename
file delete @ws/name@draft --name X Delete a file
file set-entrypoint @ws/name@draft --name X [--clear] Mark (or unmark) a file as an entrypoint
eval get @ws/name@version [--file PATH] Print the version's eval YAML (skeleton if none)
eval validate @ws/name@version --file PATH Parse + type-check an eval YAML; saves nothing
eval push @ws/name@version --file PATH Validate, then save the eval
eval delete @ws/name@version Remove the eval from a version
eval run @ws/name@version [--watch] Enqueue a run; --watch streams to completion (CI gate)
eval runs @ws/name@version List recent runs, newest first
eval show <run-id> / eval watch <run-id> Summarise or follow a single run
eval cases <run-id> [--failed] Per-case pass/fail table for a succeeded run
eval case <run-id> <index> [--prompts] One case's inputs, output, assertions, and judges
dataset create @ws/name [--description ...] Create a dataset and its initial draft (private)
dataset list @ws / dataset get @ws/name List datasets, or show one with its versions
dataset update @ws/name --description "..." Edit the description
dataset dump @ws/name@version --to ./dir Export schema.json, cases.jsonl, and dataset.yaml to a directory
dataset cases push @ws/name@draft --file cases.jsonl Upload cases (JSONL/JSON/CSV); schema inferred on first upload
dataset cases pull @ws/name@version [--to cases.jsonl] Download a version's cases as JSONL
dataset schema get / set @ws/name@version [--file PATH] Read or refine a draft's JSON Schema
dataset version draft @ws/name New draft, carrying forward the latest published schema + cases
dataset version list @ws/name [--status DRAFT|PUBLISHED] List a dataset's versions
dataset version validate @ws/name@draft Check every case against the schema (non-zero exit on a violation)
tool create @ws/name [--description ...] Create a tool contract and its initial draft (private)
tool list @ws / tool get @ws/name List tools, or show one with its dependents and versions
tool update @ws/name --description "..." Edit the catalog blurb (never sent to the model)
tool dump @ws/name@version --to ./dir Export both schemas, the model description, README and metadata
tool version draft @ws/name New draft, carrying the latest published contract forward
tool version list @ws/name [--status DRAFT|PUBLISHED] List a tool's versions
tool version get @ws/name@version Show the model description and both schemas
tool version delete @ws/name@draft Delete a draft version
tool version set-description @ws/name@draft [--content | --file] Set the model-facing description (versioned)
tool version set-readme @ws/name@draft [--content | --file] Set the README
tool version set-metadata @ws/name@draft [--from-file | --string K=V] Replace or patch free-form metadata
tool schema get / set @ws/name@version [--output] [--file PATH] Read or replace the input (or --output) JSON Schema
version tools list @ws/name@version List the tool contracts a version pins
version tools add @ws/name@draft @ws/tool@^1.0.0 [--as NAME] Pin a tool version (constraint resolved once, at link time)
version tools rename @ws/name@draft @ws/tool --as NAME Change the wire name the model sees
version tools remove @ws/name@draft @ws/tool Remove a pin from a draft

A tool contract is the model-facing definition of a tool — its wire name, the description that steers when it gets called, and the JSON Schema of its arguments — versioned like a prompt. A prompt version pins concrete tool versions, and sufleur generate turns those pins into typed bindings, provider-neutral definitions and a dispatch_tool that validates what the model sent. tool schema set checks a schema locally first, against the subset the generators can express, so nothing silently degrades to unknown/Any. As with prompts, publishing a tool version and changing its visibility are done in the web app.

An eval scores a prompt version against a dataset — judges, CEL assertions, and a passing threshold. Datasets are versioned collections of test cases, authored from the CLI too (the dataset … commands above) and referenced from the eval YAML via dataset.ref. Like prompts, publishing a dataset version and changing its visibility are done in the web app. Full guides: https://sufleur.com/docs/evals and https://sufleur.com/docs/datasets.

Render before publishing

sufleur prompt render <dir> --entrypoint <name> [--vars '{...}' | --vars-file path.json] runs the same Mustache pipeline as the generated runtime — useful for previewing a draft locally before publishing, or for quick experimentation against a version dump directory. No auth required. For a decision prompt, pass --question <id> instead to print one rendered question (add --options '{...}' for an open choice).

Invocation modes

The sufleur command on your PATH is the Go binary itself. For tools that prefer module-style invocation:

python -m sufleur_cli --help

This goes through a tiny Python wrapper that locates the binary and os.execvps it (POSIX) or subprocess.runs it (Windows). Slightly slower because Python boots first, but useful when invoking the CLI programmatically from a Python tool that wants to be sure it's calling the binary in the active environment.

The find_sufleur_bin() helper is also importable:

from sufleur_cli import find_sufleur_bin
print(find_sufleur_bin())  # absolute path to the binary

Supported platforms

OS Architectures
macOS x86_64, arm64
Linux x86_64, aarch64 (manylinux 2.17 / glibc 2.17+)
Windows x86_64, arm64

Alpine / musl libc is currently unsupported (no musllinux wheel) — pip will refuse with "no matching distribution" rather than silently producing a broken install. There is no source distribution.

License

MIT.

Metadata

Release files for sufleur-cli 0.8.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for sufleur-cli 0.8.0
File
sufleur_cli-0.8.0-py3-none-win_arm64.whl Python 3 none Windows ARM64 Details
sufleur_cli-0.8.0-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
sufleur_cli-0.8.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details
sufleur_cli-0.8.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64 Details
sufleur_cli-0.8.0-py3-none-macosx_11_0_x86_64.whl Python 3 none macOS 11.0+ x86-64 Details
sufleur_cli-0.8.0-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details

Total release size: 42.9 MB

Release files / sufleur_cli-0.8.0-py3-none-win_arm64.whl

Download URL sufleur_cli-0.8.0-py3-none-win_arm64.whl
Size 6.9 MB
Tags Python 3 Windows ARM64
SHA-256 checksum
How to use checksums
9f24857aa9b53363b531b86fe37be76f79d22fadc30497897ea2980a34612ae2
BLAKE2b-256 checksum
How to use checksums
b7a102dff6f61fa4fcd0a45a4bf8ce2f4ba1e38e5e6ab89718cbcf37fd4a1164
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.

Transparency log

Release files / sufleur_cli-0.8.0-py3-none-win_amd64.whl

Download URL sufleur_cli-0.8.0-py3-none-win_amd64.whl
Size 7.5 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
8994c750cb60cd62ae77be15e100cbc47cce486ce63be0c53645ee7051852c86
BLAKE2b-256 checksum
How to use checksums
f2fde29024733a509366d8cd3ec47d4e265ebedd367c3ed782343949b12d7748
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.

Transparency log

Release files / sufleur_cli-0.8.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL sufleur_cli-0.8.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 7.4 MB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
bd94673bc95bdfcd1aa4b330336963fba1d8c5a21040d3babeb28807892fda92
BLAKE2b-256 checksum
How to use checksums
d5fe66f001b382a009540b7d3de362249f81fe3638cd8f3e8e49cd8a8f1d52ce
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.

Transparency log

Release files / sufleur_cli-0.8.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL sufleur_cli-0.8.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 6.8 MB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
0f98a4d069510daefb8ff1f7f28b64b7ffa36cb05c0222665e71fb0eb2db6c76
BLAKE2b-256 checksum
How to use checksums
246d7bc673404207fb2f856d644730f95ab6126d88664f3106f6de9377576ab5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.

Transparency log

Release files / sufleur_cli-0.8.0-py3-none-macosx_11_0_x86_64.whl

Download URL sufleur_cli-0.8.0-py3-none-macosx_11_0_x86_64.whl
Size 7.3 MB
Tags Python 3 macOS 11.0+ x86-64
SHA-256 checksum
How to use checksums
c13eb43c83338afa695872598ac635e194ad81fa1748aa018d8cbfb850a29664
BLAKE2b-256 checksum
How to use checksums
afd8b03a3ad281d22f4b0eacb75ecd4b29a4a1883c57b1087e4bbfa787504a94
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.

Transparency log

Release files / sufleur_cli-0.8.0-py3-none-macosx_11_0_arm64.whl

Download URL sufleur_cli-0.8.0-py3-none-macosx_11_0_arm64.whl
Size 6.9 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
e19209ad2f280974b8a9841c475eb7c0b107fea3ec0cbe5e59653598a57eb3d9
BLAKE2b-256 checksum
How to use checksums
76fe8b8bef8e24d2917a54a694a036d3c808c2656b0e0ffe52dbaedf13c06a68
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.

Transparency log

Release history Release notifications | RSS feed

0.9.0

6 release files

This release

0.8.0 This release

6 release files

0.7.0

6 release files

0.6.0

6 release files

0.5.0

6 release files

0.4.0

6 release files

0.3.0

6 release files

0.2.1

6 release files

0.2.0

6 release files

0.1.0

6 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page