Penstock

Command line / Reference

Every command

Every penstock command, one by one: what it does, its options, an example, what it needs and its exit codes.

Free

All commands at a glance

CommandWhat it doesNeeds
initSet up a project.Free
doctorCheck the setup.Free
checkEverything wrong with the diagrams.Free. Flow, SARIF, baselines: Pro, Team in a pipeline
migrateCamunda 7 to Camunda 8.Free. convert: Pro, Team in a pipeline
codeDiagrams against code.Pro, Team in a pipeline
templates generateWrite an element template for every job worker.Pro, Team in a pipeline
templates checkCheck the templates against the workers.Free
scenarios listEvery way through each process.Pro, Team in a pipeline
scenarios runRun the test-case files on an engine.Pro, Team in a pipeline
scenarios generateWrite the test-case files from the ways through.Pro, Team in a pipeline
junitWrite a JUnit test for every way through.Pro, Team in a pipeline
layoutAuto layout.Pro, Team in a pipeline
fmtFormat the XML.Free
diffWhat changed.Free. --images: Pro, Team in a pipeline
exportPictures of diagrams.Free
shareOne HTML file.Free
importFrom draw.io, Visio, Signavio, Bizagi.Free
docsDocumentation pages.Pro, Team in a pipeline
reportA pull request comment.Pro, Team in a pipeline
botThe review bot.Pro, Team in a pipeline
deploySend to the engine.Free
showList a diagram's elements.Free
editChange a diagram.Pro, Team in a pipeline (--schema is free)
licenseBuy, sign in, status.Free
trustLet a project run its hooks.Free
mcpServe diagrams to an AI agent.Free to read. Other tools: Pro, Team in a pipeline
connectorsCamunda connector templates.Free
updateMove the wrapper to a version.Free

A command marked Pro, Team in a pipeline is a paid one. One rule decides what it needs: Pro on your machine, Team in a pipeline, which is any run where CI is set. See Paid commands. penstock help and penstock --version print the overview and the version, and penstock <command> --help prints the page of one command, with the same options, example, tier and exit codes as here.

Options that many commands share

--profile <name>Use this profile, as PENSTOCK_PROFILE would. Every command takes it. See Profiles.
--format text|json|sarif|github|mdHow a command reports itself. Each command takes only some of these, and says which below.
--out <dir>, -oWhere a command puts what it writes. For share and import, -o is the file.
--lang <tag>The language of the answer: en, ru, de, zh, ja, ko, es, pt-BR or fr. Without it LANG, then LC_MESSAGES, then LC_ALL decide, and English is the fallback.
--helpPrint the help and stop. The help ends with a line, The guide:, and the address of this guide.
--versionPrint the version.

Exit codes

Every command lists its own exit codes below. They follow one rule.

0The command did what it was asked, and there was nothing to act on.
1Something a person can act on: findings, a path that is not there, a file that is not a diagram, a value an option does not take, a licence this machine does not have.
2The command line itself failed, not the command. The message says what.
130Only scenarios run and scenarios generate: Ctrl and C twice.

A command that produces a findings table and finds something exits with 1 and still writes the table to standard output. A refusal, such as a missing path or a licence message, goes to standard error. A machine format (json, sarif, github) is always written to standard output, even when it reports errors.

init

Write a penstock.yaml for this project: the engine it runs on, the rules, the profiles and the deployment. Asked in a terminal when the folder is empty and nothing says otherwise.

penstock init [folder] [--engine camunda-8|camunda-7|operaton|cib-seven|classic] [--ci github|gitea|gitlab|bitbucket|azure] [--hooks]
  1. Writes penstock.yaml with the engine you chose, or guessed from your diagrams. If a file exists, it is kept and the command says so.
  2. Adds penstock.local.yaml to .gitignore.
  3. Installs the wrapper: penstockw, penstockw.cmd and penstock/wrapper/penstock-wrapper.properties. See The wrapper.
OptionWhat it does
--engine <name>camunda-8, camunda-7, operaton, cib-seven or classic.
--example <name>camunda-8 or camunda-7, to write a small runnable project into an empty folder.
--ci <name>github, gitea, gitlab, bitbucket or azure: write a pipeline for the tier this machine has.
--hooksInstall a pre-commit hook that formats and checks staged diagrams.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

In a terminal, on an empty folder with no --engine, init asks which engine with a numbered choice. Anywhere else it guesses from the diagrams. --example writes a small runnable project into an empty folder: see The example project. --ci also writes a pipeline for the licence on this machine, and forgejo is accepted too although the help does not list it: see Pipelines, reports and the bot. --hooks installs a pre-commit hook that formats and checks the diagrams a commit carries. It lives in .git/hooks, so it is yours, and deleting the file stops it. It runs check --baseline, which needs Pro.

Example

$ penstock init --engine camunda-8
A terminal running penstock init --engine camunda-8. The output lists wrote penstock.yaml with engine camunda-8, updated .gitignore with penstock.local.yaml, and wrote penstockw, penstockw.cmd and penstock/wrapper/penstock-wrapper.properties
What init writes into a folder: the file, the .gitignore line and the wrapper.

Needs: Free

Exit codeMeaning
0The file was written.
1The folder could not be written, or the file is already there.
2The CLI itself failed, not the command; the message says what.

doctor

Check the setup: which configuration layers are in force, which profiles, rules, element templates and engine.

penstock doctor [folder] [--format json]
$ penstock doctor
node        v26.10.0
root        /private/tmp/pex
config      penstock.yaml
engine      camunda-8
profiles    (none)
deployment  http://localhost:8080/v2 - answered 404
OptionWhat it does
--format <text|json>json prints the configuration as it resolved, for a tool to read.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

It prints the Node version, the project root, the configuration files that apply, any problem in them, the engine, the profiles (a star marks the one in force), the rules file and the element template paths (marked missing when they are not there), and whether the deployment address answers, with a 4 second timeout. It also says when the deployment credentials are incomplete. --format json prints the settings as they resolve after every layer.

Example

$ penstock doctor --format json
A terminal running penstock doctor. It prints node, root, config penstock.yaml, engine camunda-8, profiles prod, test, and deployment http://localhost:8090/v2 as unreachable with the reason fetch failed
The doctor of a project with two profiles, run when no engine answers at the address.

Needs: Free

Exit codeMeaning
0The setup was read.
1Something in it could not be read, and the answer says what.
2The CLI itself failed, not the command; the message says what.

check

Everything wrong with the diagrams, in sections: the drawing itself, the bpmnlint rules and, on Pro, how the processes, decisions and forms actually run.

penstock check [paths...] [--only drawing,rules,flow] [--format text|json|sarif|github] [--baseline] [--update-baseline]
$ penstock check .
ok    src/main/resources/approve-order.form
fail  src/main/resources/order-process.bpmn
        OVERLAP: approve_order <-> charge_card
        note bpmnlint/no-overlapping-elements: Element overlaps with other element (approve_order)
ok    src/main/resources/shipping-method.dmn

Checks .bpmn, .dmn and .form files in three sections: drawing, rules (bpmnlint, and the engine compatibility rules) and flow (how the processes, decisions and forms run). Only errors fail the command. The form check is lint and is free. Flow analysis of processes and decisions is paid. --update-baseline writes the findings to penstock-baseline.json: commit it, and refresh it after an upgrade. --trust-project lets this project's hooks run for this one run, without recording anything. Hooks: preCheck and postCheck. See Diagram check, Validation and Flow analysis.

OptionWhat it does
--only <sections>which parts to run: drawing, rules, flow.
--format <format>text, json, sarif or github; sarif and github are for a code review.
--baselineFail only on findings the baseline does not already accept.
--update-baselineWrite the current findings as the baseline.
--trust-projectLet this project's penstock.yaml run its hooks for this run, without writing anything down.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock check --only rules src/orders
A terminal running penstock check. diagrams/credit-hold.bpmn fails with three errors and two warnings, each with its rule id and the element id. invoice-approval.form, order-to-cash.bpmn and shipping-decision.dmn are ok. A line points to the page of the rules
penstock check over a project. A file with findings says fail and lists them; the others say ok.

Needs: Free With --only flow, --baseline or --format sarif|github: Pro, Team in a pipeline

Exit codeMeaning
0Nothing was found, or everything found is in the baseline.
1Something was found that a person can act on.
2The CLI itself failed, not the command; the message says what.

migrate analyze

Read the Camunda 7 diagrams and say what does not carry over to Camunda 8, and why.

penstock migrate analyze [paths...] [--format text|json|sarif|github] [--baseline] [--update-baseline]

penstock migrate [analyze|convert|report] [paths]. Without a word it runs analyze. The full description is on Camunda 7 to 8. All three take the options below; --target takes a version from 8.5 to 8.9, and the help names 8.8 as the default.

OptionWhat it does
--format <format>text, json, sarif or github.
--out <dir>Where the report, or the converted files, are written.
--dry-runSay what would be written and write nothing.
--target <version>The Camunda 8 version to convert for, 8.8 by default.
--fail-on <error|warning>What makes the exit code 1: error, or warning.
--baselineFail only on findings the baseline does not already accept.
--update-baselineWrite the current findings as the baseline.
--verboseList and annotate what only carries over as it is.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock migrate analyze src/orders

Needs: Free With --baseline or --format sarif|github: Pro, Team in a pipeline

Exit codeMeaning
0Nothing would be lost, or everything lost is in the baseline.
1Something would be lost.
2The CLI itself failed, not the command; the message says what.

migrate convert

Write the Camunda 8 files into a camunda-8 folder next to the one given, with a penstock.yaml of their own. Nothing else in the project is touched.

penstock migrate convert [paths...] [--target 8.5..8.9] [--out <dir>] [--dry-run]

Writes the Camunda 8 files into a camunda-8 folder next to the one given, with a penstock.yaml of their own that says engine: camunda-8. Nothing else in the project is touched, so the Camunda 7 files stay as they are. --out chooses another folder. --dry-run says what would be written and writes nothing.

OptionWhat it does
--format <format>text, json, sarif or github.
--out <dir>Where the report, or the converted files, are written.
--dry-runSay what would be written and write nothing.
--target <version>The Camunda 8 version to convert for, 8.8 by default.
--fail-on <error|warning>What makes the exit code 1: error, or warning.
--baselineFail only on findings the baseline does not already accept.
--update-baselineWrite the current findings as the baseline.
--verboseList and annotate what only carries over as it is.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock migrate convert --target 8.8 --dry-run src/orders

Needs: Pro, Team in a pipeline Analysis and the report are free.

Exit codeMeaning
0The files were written, or --dry-run found nothing to write.
1Something could not be converted, and the answer says which.
2The CLI itself failed, not the command; the message says what.

migrate report

Write the migration report to hand on: what each diagram became, and what did not come across.

penstock migrate report [paths...] [--out <dir>]

Writes the migration report, Markdown and one self-contained HTML page: what each diagram became and what did not come across.

OptionWhat it does
--format <format>text, json, sarif or github.
--out <dir>Where the report, or the converted files, are written.
--dry-runSay what would be written and write nothing.
--target <version>The Camunda 8 version to convert for, 8.8 by default.
--fail-on <error|warning>What makes the exit code 1: error, or warning.
--baselineFail only on findings the baseline does not already accept.
--update-baselineWrite the current findings as the baseline.
--verboseList and annotate what only carries over as it is.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock migrate report --out docs

Needs: Free

Exit codeMeaning
0The report was written.
1It could not be written, and the answer says where it tried.
2The CLI itself failed, not the command; the message says what.

code

Check the diagrams against the code: job types, topics, classes and beans with nothing to run them, and workers no diagram uses.

penstock code [paths...] [--format text|json]

Reports a job type, topic, class or bean that nothing runs, a called process no diagram defines, and a worker no diagram uses, each with file and line. Reads the repositories in linked. See Code links.

OptionWhat it does
--format <text|json>How the findings are reported.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock code src/orders
A terminal running penstock code. It prints 1 source file, 2 diagrams, 12 references from diagrams to code, and the line Every task the diagrams hand to code has something in the code to run it
penstock code on a project where every task has code to run it.

Needs: Pro, Team in a pipeline

Exit codeMeaning
0Every worker and every reference lines up.
1Something does not, and the answer names it.
2The CLI itself failed, not the command; the message says what.

templates generate

Write an element template for every job worker the project has, into .camunda/element-templates, so the Camunda palette fills itself in.

penstock templates generate [folder]

Reads the Java, Kotlin, Groovy and Scala sources under the folder, finds every job worker, and writes an element template for each into .camunda/element-templates. A template that exists is updated, not replaced. It lists each template with its worker, file and line, the number of inputs, and what it did. With no worker in the project it exits with 1. --check writes nothing and does what templates check does. The editors do the same for one worker with Create element template. See Element templates.

OptionWhat it does
--checkCheck instead of write: the same as templates check, and writes nothing.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock templates generate
A terminal running penstock templates generate, then penstock templates generate --check. The first lists nine templates, each with its worker, the file and line, and the number of inputs, all written, and ends 9 templates written, 0 updated. The second says Every element template still matches the worker it was written from
Generate writes one template per worker. The check says whether they still match.

Needs: Pro, Team in a pipeline

Exit codeMeaning
0Every worker has a template.
1A worker could not be written, and the answer names it.
2The CLI itself failed, not the command; the message says what.

templates check

Check that every element template still says what its job worker asks for, and that every worker has one. Writes nothing.

penstock templates check [folder]

Checks that every element template still asks for what its worker reads, and that every worker has one. A template that has drifted is listed with what it is missing, what it has extra and what has another type, and the exit code is 1, so it works in a pipeline. With no templates in .camunda/element-templates it says so and exits with 0. It writes nothing.

OptionWhat it does
--checkWhat templates generate takes to do this instead of writing.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock templates check

Needs: Free

Exit codeMeaning
0Every template still matches its worker.
1A template has drifted, and the answer says how.
2The CLI itself failed, not the command; the message says what.

scenarios list

List every distinct way through each process, with the conditions it takes. This is a test plan.

penstock scenarios list [paths...] [--format text|json|md]

A test plan, in words: the path and the conditions that decide it. --format md writes a checklist a person can work through. --format json is for tools. See Process tests and coverage.

OptionWhat it does
--format <text|json|md>How the scenarios are listed; md is a page a person reads.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock scenarios list --format md

Needs: Pro, Team in a pipeline

Exit codeMeaning
0The scenarios were listed.
1A process could not be read, and the answer names it.
2The CLI itself failed, not the command; the message says what.

scenarios run

Run the Camunda test-case files of the diagrams against the engine they name, step by step, with the mocks the file asks for.

penstock scenarios run [paths...] [--timeout <seconds>] [--junit-xml <file>] [--junit] [--teamcity]

Runs the Camunda test-case files of the diagrams against the engine they name, step by step, with the mocks the file asks for. Every case is checked against the Camunda test-case schema before the run, and a run deploys a test build of its own, prefixed penstock.<run>., so the definitions you deployed are not touched. In a pipeline the engine needs deployment.forTests: true. Press Ctrl and C once to ask the engine to stop and keep going, and twice to cancel the running case and remove its build. See Scenarios.

OptionWhat it does
--timeout <seconds>How long one step may take, 30 by default.
--junit-xml <file>Where a JUnit XML report is written.
--junitWrite a Camunda Process Test class that runs them instead.
--teamcityReport as TeamCity service messages, as the IDE reads them.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock scenarios run --junit-xml target/scenarios.xml
A terminal running penstock scenarios run on a machine without a licence. It says Process test runs are part of Penstock Pro, and gives two commands: penstock license buy and penstock license login
What a paid command prints without a licence.

Needs: Pro, Team in a pipeline

Exit codeMeaning
0The run finished with no failing case.
1A case failed, or the engine could not be reached.
2The CLI itself failed, not the command; the message says what.
130Ctrl-C twice: the case is cancelled, its build removed, and the run stops.

scenarios generate

Write the Camunda test-case files from the ways through each process, which scenarios run then plays back against an engine.

penstock scenarios generate [paths...]

Writes the Camunda test-case files from the ways through each process: one scenario for every way through, with example variables and a completing mock for each task. A process that starts on a message, a timer or a signal is planned from that start event, and a start kind that cannot be expressed as a scenario is said in the review rather than left out quietly. Cases already in a file are kept. See Scenarios.

The old names still work and are left out of the help: paths is scenarios list, test-cases is scenarios run, test-cases --generate is scenarios generate, and tests is junit.

OptionWhat it does
--timeout <seconds>How long one step may take, 30 by default.
--junit-xml <file>Where a JUnit XML report is written.
--teamcityReport as TeamCity service messages, as the IDE reads them.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock scenarios generate src/orders

Needs: Pro, Team in a pipeline

Exit codeMeaning
0The run finished with no failing case.
1A case failed, or the engine could not be reached.
2The CLI itself failed, not the command; the message says what.
130Ctrl-C twice: the case is cancelled, its build removed, and the run stops.

junit

Write a JUnit test for every way through each process, with the variables that take it there, for Camunda Process Test or camunda-bpm-assert.

penstock junit [paths...] [--out <dir>] [--package <name>]

Writes a JUnit test for every way through each process, with the variables that take it there, for Camunda Process Test or camunda-bpm-assert. The command names the test dependencies your build lacks, using each engine's own Java packages: org.camunda for Camunda 7, org.operaton for Operaton, org.cibseven for CIB seven. See Process tests and coverage.

OptionWhat it does
--out <dir>Where the tests are written.
--package <name>The package the tests are written in.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock junit --out src/test/java --package com.acme.orders

Needs: Pro, Team in a pipeline

Exit codeMeaning
0The tests were written.
1A test could not be written, and the answer names the process.
2The CLI itself failed, not the command; the message says what.

layout

Lay the diagrams out as a readable left-to-right flow, whatever they were drawn like.

penstock layout [paths...] [--write]
$ penstock layout .
would change src/main/resources/order-process.bpmn

Auto layout always reflows, so a difference here is expected; pass --write to take it.

Lays the diagrams out as a readable left-to-right flow, whatever they were drawn like. Without --write the answer is the plan and nothing is changed. Hooks: preLayout, postLayout. See Auto layout.

OptionWhat it does
--writeSave what was laid out; without it the answer is the plan and the file is untouched.
--trust-projectLet this project's penstock.yaml run its hooks for this run, without writing anything down.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock layout --write src/orders

Needs: Pro, Team in a pipeline

Exit codeMeaning
0The diagrams do not overlap and nothing runs through a shape.
1A diagram could not be laid out, and the answer says which.
2The CLI itself failed, not the command; the message says what.

fmt

Rewrite the XML the way the editor saves it, so a diff of a diagram stays readable.

penstock fmt [paths...] [--write]

Rewrites the XML the way the editor saves it, so a renamed task is a one-line diff. Without --write it lists each file as same or would rewrite and leaves the files alone, and it exits with 1 if any file would change, so it also works as a check in a pipeline.

OptionWhat it does
--writeSave the result; without it the answer is the diff and the file is untouched.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock fmt --write src/orders

Needs: Free

Exit codeMeaning
0Every file is already formatted, or was.
1A file could not be parsed, and the answer names it.
2The CLI itself failed, not the command; the message says what.

diff

What changed between two versions of a process: two files, or what the working tree changed since a commit, a branch or a tag.

penstock diff <before> <after> | penstock diff [<ref>] [paths...] [--images]
penstock diff before.bpmn after.bpmn     # two files
penstock diff main                       # working tree against a branch, tag or commit
penstock diff HEAD~3 src/orders          # limit to some paths
penstock diff main --images              # pictures with changes coloured
$ penstock diff HEAD
src/main/resources/order-process.bpmn
  changed  ServiceTask "Charge the card" charge_card (name)

Lists added, removed and changed elements by name, and says only the drawing moved when that is all. Without a reference it compares against HEAD~1. --out chooses where the pictures go, .penstock/images by default. It needs a Git repository for a reference.

OptionWhat it does
--imagesRender before and after, with the changes coloured.
--out <dir>Where the pictures are written.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock diff HEAD~1 --images

Needs: Free With --images: Pro, Team in a pipeline

Exit codeMeaning
0Nothing changed.
1Something did, and the answer lists it.
2The CLI itself failed, not the command; the message says what.

export

Render the diagrams as pictures, one file per diagram.

penstock export [paths...] [--format svg|png] [--out <dir>]

Renders the diagrams as pictures, one file per diagram. SVG is drawn by Penstock from the coordinates and needs no browser. --format png needs puppeteer installed in the project. See Export.

OptionWhat it does
--format <svg|png>svg or png; the format the export is asked for.
--out <dir>Where the pictures are written.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock export --format png --out docs/diagrams

Needs: Free

Exit codeMeaning
0Every diagram was written.
1A diagram could not be rendered, and the answer names it.
2The CLI itself failed, not the command; the message says what.

share

One HTML file to send: a read-only viewer with zoom, pan and the documentation of each element. It opens offline, with nothing else on the page.

penstock share <file> [--out <file.html>]

Takes one .bpmn, .dmn or .form file and writes one HTML file: a read-only viewer with zoom, pan and the documentation of each element. It opens offline in any browser and loads nothing else. -o is the same as --out. See Share as HTML.

OptionWhat it does
--out <file.html>The file to write; -o is the same.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock share src/orders/order.bpmn -o order.html

Needs: Free

Exit codeMeaning
0The file was written.
1It could not be written, and the answer says where it tried.
2The CLI itself failed, not the command; the message says what.

import

Turn a draw.io, Visio, Signavio or Bizagi diagram into BPMN 2.0, and list what could not be mapped.

penstock import <file> [--out <file.bpmn>] [--force] [--format text|json]

Turns a draw.io, Visio, Signavio or Bizagi diagram into BPMN 2.0 and prints what was not imported, what was guessed and what was not carried over. -o is the same as --out. See Import a diagram.

OptionWhat it does
--out <file.bpmn>The file to write; -o is the same.
--forceReplace a file that is already there.
--format <text|json>json reports what could not be mapped as a document a tool can read.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock import legacy.drawio -o order.bpmn
A terminal running penstock import drawio-sample.drawio. It wrote drawio-sample.bpmn, says Imported from draw.io: 4 elements, 3 flows, lists two guesses where a circle became a start event and an end event, and a line about the routes of the connectors
The report of an import: what was written, what was counted and what was guessed.

Needs: Free

Exit codeMeaning
0The diagram was written.
1Something could not be mapped, and the answer says what.
2The CLI itself failed, not the command; the message says what.

docs

Write documentation for every process, decision file and form: a page each, with the picture, the steps, the decisions, the events and the data.

penstock docs [paths...] [--format html|md] [--out <dir>]
$ penstock docs . --out docs-out
wrote docs-out/approve-order.html
wrote docs-out/order-process.html
wrote docs-out/shipping-method.html
wrote docs-out/index.html

3 diagram(s) documented.

A page for each process: its picture, steps with what runs them and who, decisions and conditions, events, the data each step sets and reads, and whether it runs to its end. A page for each .dmn file with every decision's rules as a table, its hit policy in words and the table check. A page for each form with the variable every field writes. HTML by default, --format md for a wiki.

OptionWhat it does
--format <html|md>html or md.
--out <dir>Where the pages are written.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock docs --format md --out docs/processes

Needs: Pro, Team in a pipeline

Exit codeMeaning
0The pages were written.
1A page could not be written, and the answer names it.
2The CLI itself failed, not the command; the message says what.

report

One markdown comment for a pull request: the check, the diff and a picture of every diagram that changed.

penstock report [folder] [--base <ref>] [--out <file.md>] [--images-dir <dir>]

Writes one Markdown comment for a pull request: the check, what changed and a picture of every diagram that changed, with the changes outlined. The defaults are origin/main for --base, penstock-report.md for --out and .penstock/images for --images-dir. --image-base is where the pictures will be served from, so the comment shows them, and --images-link is where to find them instead when they cannot be shown inline. It needs a Git repository. See Pipelines, reports and the bot.

OptionWhat it does
--base <ref>What the report compares against, origin/main by default.
--out <file>Where the markdown is written.
--images-dir <dir>Where the pictures are written.
--image-base <url>Where the pictures will be served from, for a comment that shows them.
--images-link <url>Where to find them instead, when they cannot be shown inline.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock report --base origin/main --out penstock-report.md

Needs: Pro, Team in a pipeline

Exit codeMeaning
0The report was written.
1It could not be written, and the answer says where it tried.
2The CLI itself failed, not the command; the message says what.

bot

Answer in a pull or merge request, from the CI run it is in. penstock init --ci sets the pipeline up.

penstock bot [folder] [--stage draw|post]

Answers in a pull or merge request, from the CI run it is in. --stage draw draws the pictures now and --stage post posts later, when the pictures are uploaded between. penstock init --ci writes the pipeline. See Pipelines, reports and the bot.

OptionWhat it does
--stage <draw|post>Draw the pictures now and post later, when they are uploaded between.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock bot --stage draw

Needs: Pro, Team in a pipeline

Exit codeMeaning
0The answer was posted, or the pictures were drawn.
1It could not be, and the answer says why.
2The CLI itself failed, not the command; the message says what.

deploy

Deploy the processes, decisions and forms to the engine the configuration names.

penstock deploy [paths...] [--dry-run] [--wait]
$ penstock deploy . --dry-run
would send 3 file(s) to http://localhost:8080/v2/deployments as "order-example"
      src/main/resources/approve-order.form
      src/main/resources/order-process.bpmn
      src/main/resources/shipping-method.dmn

Deploys the processes, decisions and forms to the engine the configuration names. Files are grouped by the target each folder asks for, and a file with no deployment.url is skipped. --wait asks the engine until it shows the definitions: “deployed” and “running” are not the same claim, and it prints live order-process v3. Secrets come from PENSTOCK_PASSWORD, PENSTOCK_CLIENT_SECRET and PENSTOCK_TOKEN. Any PENSTOCK_* variable that matches a setting overrides it for the run. Hooks: preDeploy, postDeploy. See Deployment.

Before anything is sent, deploy says so when deployment.auth.type is basic and there is no PENSTOCK_PASSWORD, rather than letting the engine refuse it. An answer of 401 or 403 says which of deployment.auth in penstock.local.yaml, PENSTOCK_PASSWORD, PENSTOCK_CLIENT_SECRET or PENSTOCK_TOKEN to set. A password: key in penstock.local.yaml is not read.

OptionWhat it does
--dry-runSay what would be sent, and send nothing.
--waitConfirm with the engine that the definitions are live.
--trust-projectLet this project's penstock.yaml run its hooks for this run, without writing anything down.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock deploy --dry-run src/orders

Needs: Free

Exit codeMeaning
0The engine took the deployment.
1It did not, and the answer says what it said.
2The CLI itself failed, not the command; the message says what.

show

List a diagram's elements, flows and properties, for a person or for a tool to read.

penstock show <file> [--format text|json]
$ penstock show order-process.bpmn
order-process.bpmn  (camunda-8)

StartEvent             order_received "Order received"
ServiceTask            charge_card "Charge card"
                         jobType = charge-card
SequenceFlow           Flow_2  approve_order -> charge_card

Lists a diagram's elements, flows and properties, for a person or for a tool to read. The ids it prints are the ones edit refers to.

OptionWhat it does
--format <text|json>json is the same document a tool reads.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock show src/orders/order.bpmn --format json
A terminal running penstock show diagrams/order-to-cash.bpmn. A table with the columns TYPE, ID, NAME and FLOW lists the start event, the gateways, the tasks, the intermediate catch event, the end event and the sequence flows with the elements they join
show prints every element and flow with its id.

Needs: Free

Exit codeMeaning
0The diagram was read.
1It is not a diagram, or not there, and the answer says which.
2The CLI itself failed, not the command; the message says what.

edit

Add, connect, set or remove in a diagram the way the editor would, and refuse an edit that would break it.

penstock edit <file> <add|connect|set|remove> ... [--ops <json>] [--ops-file <file|->] [--schema]
penstock edit orders.bpmn add service-task --after Task_check --name "Reserve stock" --set jobType=reserve-stock
penstock edit orders.bpmn connect Gateway_stock Task_notify --condition "=not(inStock)"
penstock edit orders.bpmn set Task_notify --set assignee=support --input customer==order.customer
penstock edit orders.bpmn remove Task_legacy

Adds, connects, sets or removes in a diagram the way the editor would. A new element is placed and connected by the editor's own modelling, gets an id from your conventions, and carries only properties your engine knows. After an edit the diagram is read back and checked. An edit that would break the drawing (shapes on top of each other, a flow through a shape, a flow torn from its element) is not written, and nothing is written when the operations cannot be read. A crossing is reported but allowed. --ops and --ops-file take the same changes as JSON, which is the contract: several operations apply together or not at all. --schema prints the contract for the file's engine, and an unknown operation, field or property is refused.

OptionWhat it does
--ops <json>The operations as JSON on one line.
--ops-file <file>The operations as JSON in a file, or - to read them from standard input.
--schemaPrint the contract the operations follow, and change nothing.
--after <id>Add after this element, the way the editor places a new one.
--event <type>The event type to add.
--name <text>The name of the element that is added, connected or set.
--id <id>The id of the element to set or remove.
--condition <text>The condition of a sequence flow that is connected.
--set <name=value>An attribute to set, more than once.
--input <name>=<value>An input mapping, more than once.
--output <name>=<value>An output mapping, more than once.
--dry-runSay what would change and write nothing.
--forceWrite even when the edit would break the diagram.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock edit src/orders/order.bpmn add service-task --after Task_check --name Ship

Needs: Pro, Team in a pipeline --schema is free.

Exit codeMeaning
0The diagram was changed, or --dry-run found nothing to change.
1The edit would break the diagram, or could not be read, and nothing was written.
2The CLI itself failed, not the command; the message says what.

license status

Which licence this machine has, and why it is that one.

penstock license [status]

Nobody copies a key: buying opens Polar's checkout and signing in is an e-mail code, and both end with the licence on this machine, in ~/.penstock/license.json, which the editors share. A key covers three machines. license alone is license status: which licence this machine has and why, and during the trial, when it ends. Without a licence it also says how to buy or sign in, and shows the education offer for Free Pro when one is set up. The other commands are below. In a pipeline set PENSTOCK_LICENSE to a Team key. It only counts when CI is set. See Pipelines, reports and the bot and Buying and signing in.

OptionWhat it does
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock license status

Needs: Free

Exit codeMeaning
0The answer was read.
1The licence service could not be reached, and the machine stays as it was.
2The CLI itself failed, not the command; the message says what.

license buy

Buy Penstock. The checkout opens in a browser and the licence reaches this machine by itself once you have paid.

penstock license buy [pro|team]

Opens the checkout in a browser and waits up to an hour for the payment, then asks for the e-mail code. It needs a terminal.

OptionWhat it does
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock license buy pro

Needs: Free

Exit codeMeaning
0The licence is on this machine.
1The payment did not go through, or nothing was charged.
2The CLI itself failed, not the command; the message says what.

license login

Sign in with the e-mail you bought Penstock with. There is no key to copy.

penstock license login [email]

Signs in with the e-mail you bought Penstock with. When every place is taken it lists the machines, so you can free one.

OptionWhat it does
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock license login you@example.com

Needs: Free

Exit codeMeaning
0The licence is on this machine.
1There is no licence bought with that address.
2The CLI itself failed, not the command; the message says what.

license activate

Use a key you already have.

penstock license activate <key>

Activates a key you already have, typed by hand.

OptionWhat it does
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock license activate PENSTOCK-PRO-...

Needs: Free

Exit codeMeaning
0The licence is on this machine.
1The key was not accepted, and the answer says why.
2The CLI itself failed, not the command; the message says what.

license key

The key for PENSTOCK_LICENSE in a pipeline, found again by the e-mail you bought with, because the machine no longer keeps it.

penstock license key [email]

Prints the Team key for PENSTOCK_LICENSE in a pipeline, found again by the e-mail you bought with, because the machine no longer keeps the key. It needs a licence activated on this machine and a terminal, because it signs in with an e-mail code. Only a Team key is printed: keep it secret.

OptionWhat it does
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock license key you@example.com

Needs: Free

Exit codeMeaning
0The key was found.
1There is no Team key bought with that address, or there is no terminal to ask on.
2The CLI itself failed, not the command; the message says what.

license logout

Release this machine's licence so another machine can use it. The same as license deactivate.

penstock license logout

Releases this machine's licence so another machine can use it. license deactivate does the same.

OptionWhat it does
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock license logout

Needs: Free

Exit codeMeaning
0The licence was released, or there was no seat to release.
1The licence service could not be reached.
2The CLI itself failed, not the command; the message says what.

trust

Let this project's penstock.yaml run shell commands here. A hook of check, layout or deploy asks for this first, and a project with no penstock.yaml is not trusted at all.

penstock trust [path] [--list] [--forget] [--yes]

Lets a project's penstock.yaml run shell commands on this machine. A hook of check, layout or deploy asks for this first, and a project with no penstock.yaml is not trusted at all. In a terminal, penstock trust asks once for the project the path is in, which is the current folder by default, and records it in ~/.penstock/trusted-projects.json, keyed by its real path. The IDE plugin reads and writes the same file on IDE 2024.2 and 2025.1, so a project trusted in one is trusted in both there.

With no trust, a hook does not run. The message names the stage and the exact command that was refused, and says to run penstock trust, or to use --trust-project on check, layout or deploy to allow it for that one run. Inside a pipeline, where CI is set, hooks run without a question, because there the pipeline is the repository's own. A hook gets only four PENSTOCK_* variables: PENSTOCK_FILES, PENSTOCK_ROOT, PENSTOCK_STAGE and PENSTOCK_PROFILE. The licence key, the password and the tokens never reach it.

OptionWhat it does
--listPrint what is trusted on this machine.
--forgetTake one project back.
--yesDo not ask, which is what a pipeline wants.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock trust --list
A terminal running penstock check in a project that is not trusted. A hook line says preCheck was not run, because the project is not a trusted project, and tells you to run penstock trust or pass --trust-project. The next line says the Check stage was stopped by its pre hook. Then penstock trust --list says No project is trusted on this machine
A hook that does not run in a project nobody has trusted, and the list of trusted projects.

Needs: Free

Exit codeMeaning
0The project is trusted, forgotten, or the list was printed.
1There is no penstock.yaml there to trust, or the path is not there.
2The CLI itself failed, not the command; the message says what.

mcp

Serve BPMN diagrams to an AI agent over MCP. Reading a diagram and checking it are free; flow analysis, scenarios, code links and editing need a licence.

penstock mcp [--config]

Serves BPMN diagrams to an AI agent over MCP. --config prints the entry to paste into the agent's settings and stops. The entry goes to standard output and a hint goes to standard error. See The MCP server.

OptionWhat it does
--configPrint the client entry to paste into the agent, and stop.

Example

$ penstock mcp --config

Needs: Free list_diagrams, show_diagram and check_diagram are free; the other tools are Pro, Team in a pipeline

Exit codeMeaning
0The server stopped, or the client entry was printed.
1A tool was asked for something this machine does not have.
2The CLI itself failed, not the command; the message says what.

connectors

List Camunda's connector templates, or where they are cached on this machine. Downloaded from GitHub at most once a day.

penstock connectors [--refresh]
penstock connectors                              # list the current templates
penstock connectors resolve <id> [version]      # print one template as JSON
penstock connectors --refresh                    # ask GitHub for a newer release now

Lists Camunda's connector templates, or where they are cached on this machine. They are downloaded from GitHub at most once a day and cached in ~/.penstock/connectors/. See Element templates.

OptionWhat it does
--refreshCheck GitHub for a newer release now.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock connectors --refresh

Needs: Free

Exit codeMeaning
0The list was printed.
1The release list could not be downloaded, and the answer says when it will try again.
2The CLI itself failed, not the command; the message says what.

connectors resolve

Print one connector template, resolved to the version asked for or to the newest one in the cache.

penstock connectors resolve <id> [version]

Prints one connector template, resolved to the version asked for or to the newest one in the cache.

OptionWhat it does
--refreshCheck GitHub for a newer release first.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock connectors resolve http-json

Needs: Free

Exit codeMeaning
0The template was printed.
1There is no template with that id, and the answer says what there is.
2The CLI itself failed, not the command; the message says what.

update

Move the project's wrapper to a version, the latest by default.

penstock update [version] [--distribution <spec>]

Moves the project's wrapper to a version, the latest by default. It downloads the version into ~/.penstock/wrapper/dists, replaces penstockw and penstockw.cmd with the scripts that version ships, and writes the version into penstock/wrapper/penstock-wrapper.properties. Commit those files. It needs a wrapper in the project: run penstock init first. --distribution says where to get Penstock from, a tarball or a registry spec, and is kept in the properties file. penstock wrapper was the old name and still works. See The wrapper.

OptionWhat it does
--distribution <spec>Where to get Penstock from: a tarball or a registry spec.
--profile <name>Use this profile, as PENSTOCK_PROFILE would.

Example

$ penstock update 26.10.0

Needs: Free

Exit codeMeaning
0The wrapper was moved.
1It could not be, and the answer says which step refused.
2The CLI itself failed, not the command; the message says what.

What you see

Common problems

An option is ignored.
Check that it belongs to that command. For example --write is for layout and fmt, and --wait for deploy. penstock <command> --help lists what the command reads.
"Unknown command".
Run penstock help. The old names are still accepted and say what replaces them.
The command says a feature is part of Pro.
Run penstock license to see the licence. In a pipeline the feature needs Team. See Buying and signing in.
A hook does not run and the message names penstock trust.
The project is not trusted. Run penstock trust in it, or pass --trust-project for one run.
The engine refuses a run as not a test engine.
Set deployment.forTests: true on the profile, or run against an engine on this machine and confirm it once.
The command says the project is not a Git repository.
diff <ref> and report compare against Git history. Run them inside a checkout.