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.
All commands at a glance
| Command | What it does | Needs |
|---|---|---|
init | Set up a project. | Free |
doctor | Check the setup. | Free |
check | Everything wrong with the diagrams. | Free. Flow, SARIF, baselines: Pro, Team in a pipeline |
migrate | Camunda 7 to Camunda 8. | Free. convert: Pro, Team in a pipeline |
code | Diagrams against code. | Pro, Team in a pipeline |
templates generate | Write an element template for every job worker. | Pro, Team in a pipeline |
templates check | Check the templates against the workers. | Free |
scenarios list | Every way through each process. | Pro, Team in a pipeline |
scenarios run | Run the test-case files on an engine. | Pro, Team in a pipeline |
scenarios generate | Write the test-case files from the ways through. | Pro, Team in a pipeline |
junit | Write a JUnit test for every way through. | Pro, Team in a pipeline |
layout | Auto layout. | Pro, Team in a pipeline |
fmt | Format the XML. | Free |
diff | What changed. | Free. --images: Pro, Team in a pipeline |
export | Pictures of diagrams. | Free |
share | One HTML file. | Free |
import | From draw.io, Visio, Signavio, Bizagi. | Free |
docs | Documentation pages. | Pro, Team in a pipeline |
report | A pull request comment. | Pro, Team in a pipeline |
bot | The review bot. | Pro, Team in a pipeline |
deploy | Send to the engine. | Free |
show | List a diagram's elements. | Free |
edit | Change a diagram. | Pro, Team in a pipeline (--schema is free) |
license | Buy, sign in, status. | Free |
trust | Let a project run its hooks. | Free |
mcp | Serve diagrams to an AI agent. | Free to read. Other tools: Pro, Team in a pipeline |
connectors | Camunda connector templates. | Free |
update | Move 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|md | How a command reports itself. Each command takes only some of these, and says which below. |
--out <dir>, -o | Where 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. |
--help | Print the help and stop. The help ends with a line, The guide:, and the address of this guide. |
--version | Print the version. |
Exit codes
Every command lists its own exit codes below. They follow one rule.
0 | The command did what it was asked, and there was nothing to act on. |
1 | Something 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. |
2 | The command line itself failed, not the command. The message says what. |
130 | Only 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]
- Writes
penstock.yamlwith the engine you chose, or guessed from your diagrams. If a file exists, it is kept and the command says so. - Adds
penstock.local.yamlto.gitignore. - Installs the wrapper:
penstockw,penstockw.cmdandpenstock/wrapper/penstock-wrapper.properties. See The wrapper.
| Option | What 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. |
--hooks | Install 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
Needs: Free
| Exit code | Meaning |
|---|---|
0 | The file was written. |
1 | The folder could not be written, or the file is already there. |
2 | The 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
| Option | What 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
Needs: Free
| Exit code | Meaning |
|---|---|
0 | The setup was read. |
1 | Something in it could not be read, and the answer says what. |
2 | The 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.
| Option | What 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. |
--baseline | Fail only on findings the baseline does not already accept. |
--update-baseline | Write the current findings as the baseline. |
--trust-project | Let 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
Needs: Free With --only flow, --baseline or --format sarif|github: Pro, Team in a pipeline
| Exit code | Meaning |
|---|---|
0 | Nothing was found, or everything found is in the baseline. |
1 | Something was found that a person can act on. |
2 | The 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.
| Option | What it does |
|---|---|
--format <format> | text, json, sarif or github. |
--out <dir> | Where the report, or the converted files, are written. |
--dry-run | Say 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. |
--baseline | Fail only on findings the baseline does not already accept. |
--update-baseline | Write the current findings as the baseline. |
--verbose | List 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 code | Meaning |
|---|---|
0 | Nothing would be lost, or everything lost is in the baseline. |
1 | Something would be lost. |
2 | The 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.
| Option | What it does |
|---|---|
--format <format> | text, json, sarif or github. |
--out <dir> | Where the report, or the converted files, are written. |
--dry-run | Say 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. |
--baseline | Fail only on findings the baseline does not already accept. |
--update-baseline | Write the current findings as the baseline. |
--verbose | List 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 code | Meaning |
|---|---|
0 | The files were written, or --dry-run found nothing to write. |
1 | Something could not be converted, and the answer says which. |
2 | The 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.
| Option | What it does |
|---|---|
--format <format> | text, json, sarif or github. |
--out <dir> | Where the report, or the converted files, are written. |
--dry-run | Say 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. |
--baseline | Fail only on findings the baseline does not already accept. |
--update-baseline | Write the current findings as the baseline. |
--verbose | List 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 code | Meaning |
|---|---|
0 | The report was written. |
1 | It could not be written, and the answer says where it tried. |
2 | The 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.
| Option | What it does |
|---|---|
--format <text|json> | How the findings are reported. |
--profile <name> | Use this profile, as PENSTOCK_PROFILE would. |
Example
$ penstock code src/orders
Needs: Pro, Team in a pipeline
| Exit code | Meaning |
|---|---|
0 | Every worker and every reference lines up. |
1 | Something does not, and the answer names it. |
2 | The 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.
| Option | What it does |
|---|---|
--check | Check instead of write: the same as templates check, and writes nothing. |
--profile <name> | Use this profile, as PENSTOCK_PROFILE would. |
Example
$ penstock templates generate
Needs: Pro, Team in a pipeline
| Exit code | Meaning |
|---|---|
0 | Every worker has a template. |
1 | A worker could not be written, and the answer names it. |
2 | The 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.
| Option | What it does |
|---|---|
--check | What 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 code | Meaning |
|---|---|
0 | Every template still matches its worker. |
1 | A template has drifted, and the answer says how. |
2 | The 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.
| Option | What 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 code | Meaning |
|---|---|
0 | The scenarios were listed. |
1 | A process could not be read, and the answer names it. |
2 | The 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.
| Option | What it does |
|---|---|
--timeout <seconds> | How long one step may take, 30 by default. |
--junit-xml <file> | Where a JUnit XML report is written. |
--junit | Write a Camunda Process Test class that runs them instead. |
--teamcity | Report 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
Needs: Pro, Team in a pipeline
| Exit code | Meaning |
|---|---|
0 | The run finished with no failing case. |
1 | A case failed, or the engine could not be reached. |
2 | The CLI itself failed, not the command; the message says what. |
130 | Ctrl-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.
| Option | What it does |
|---|---|
--timeout <seconds> | How long one step may take, 30 by default. |
--junit-xml <file> | Where a JUnit XML report is written. |
--teamcity | Report 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 code | Meaning |
|---|---|
0 | The run finished with no failing case. |
1 | A case failed, or the engine could not be reached. |
2 | The CLI itself failed, not the command; the message says what. |
130 | Ctrl-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.
| Option | What 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 code | Meaning |
|---|---|
0 | The tests were written. |
1 | A test could not be written, and the answer names the process. |
2 | The 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.
| Option | What it does |
|---|---|
--write | Save what was laid out; without it the answer is the plan and the file is untouched. |
--trust-project | Let 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 code | Meaning |
|---|---|
0 | The diagrams do not overlap and nothing runs through a shape. |
1 | A diagram could not be laid out, and the answer says which. |
2 | The 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.
| Option | What it does |
|---|---|
--write | Save 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 code | Meaning |
|---|---|
0 | Every file is already formatted, or was. |
1 | A file could not be parsed, and the answer names it. |
2 | The 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.
| Option | What it does |
|---|---|
--images | Render 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 code | Meaning |
|---|---|
0 | Nothing changed. |
1 | Something did, and the answer lists it. |
2 | The 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.
| Option | What 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 code | Meaning |
|---|---|
0 | Every diagram was written. |
1 | A diagram could not be rendered, and the answer names it. |
2 | The 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.
| Option | What 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 code | Meaning |
|---|---|
0 | The file was written. |
1 | It could not be written, and the answer says where it tried. |
2 | The 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.
| Option | What it does |
|---|---|
--out <file.bpmn> | The file to write; -o is the same. |
--force | Replace 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
Needs: Free
| Exit code | Meaning |
|---|---|
0 | The diagram was written. |
1 | Something could not be mapped, and the answer says what. |
2 | The 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.
| Option | What 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 code | Meaning |
|---|---|
0 | The pages were written. |
1 | A page could not be written, and the answer names it. |
2 | The 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.
| Option | What 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 code | Meaning |
|---|---|
0 | The report was written. |
1 | It could not be written, and the answer says where it tried. |
2 | The 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.
| Option | What 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 code | Meaning |
|---|---|
0 | The answer was posted, or the pictures were drawn. |
1 | It could not be, and the answer says why. |
2 | The 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.
| Option | What it does |
|---|---|
--dry-run | Say what would be sent, and send nothing. |
--wait | Confirm with the engine that the definitions are live. |
--trust-project | Let 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 code | Meaning |
|---|---|
0 | The engine took the deployment. |
1 | It did not, and the answer says what it said. |
2 | The 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.
| Option | What 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
Needs: Free
| Exit code | Meaning |
|---|---|
0 | The diagram was read. |
1 | It is not a diagram, or not there, and the answer says which. |
2 | The 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.
| Option | What 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. |
--schema | Print 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-run | Say what would change and write nothing. |
--force | Write 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 code | Meaning |
|---|---|
0 | The diagram was changed, or --dry-run found nothing to change. |
1 | The edit would break the diagram, or could not be read, and nothing was written. |
2 | The 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.
| Option | What it does |
|---|---|
--profile <name> | Use this profile, as PENSTOCK_PROFILE would. |
Example
$ penstock license status
Needs: Free
| Exit code | Meaning |
|---|---|
0 | The answer was read. |
1 | The licence service could not be reached, and the machine stays as it was. |
2 | The 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.
| Option | What it does |
|---|---|
--profile <name> | Use this profile, as PENSTOCK_PROFILE would. |
Example
$ penstock license buy pro
Needs: Free
| Exit code | Meaning |
|---|---|
0 | The licence is on this machine. |
1 | The payment did not go through, or nothing was charged. |
2 | The 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.
| Option | What it does |
|---|---|
--profile <name> | Use this profile, as PENSTOCK_PROFILE would. |
Example
$ penstock license login you@example.com
Needs: Free
| Exit code | Meaning |
|---|---|
0 | The licence is on this machine. |
1 | There is no licence bought with that address. |
2 | The 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.
| Option | What it does |
|---|---|
--profile <name> | Use this profile, as PENSTOCK_PROFILE would. |
Example
$ penstock license activate PENSTOCK-PRO-...
Needs: Free
| Exit code | Meaning |
|---|---|
0 | The licence is on this machine. |
1 | The key was not accepted, and the answer says why. |
2 | The 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.
| Option | What it does |
|---|---|
--profile <name> | Use this profile, as PENSTOCK_PROFILE would. |
Example
$ penstock license key you@example.com
Needs: Free
| Exit code | Meaning |
|---|---|
0 | The key was found. |
1 | There is no Team key bought with that address, or there is no terminal to ask on. |
2 | The 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.
| Option | What it does |
|---|---|
--profile <name> | Use this profile, as PENSTOCK_PROFILE would. |
Example
$ penstock license logout
Needs: Free
| Exit code | Meaning |
|---|---|
0 | The licence was released, or there was no seat to release. |
1 | The licence service could not be reached. |
2 | The 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.
--listprints every trusted project with the date.--forgettakes the project back.--yesdoes not ask. It is what a script wants. With no terminal the question is not asked either.
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.
| Option | What it does |
|---|---|
--list | Print what is trusted on this machine. |
--forget | Take one project back. |
--yes | Do not ask, which is what a pipeline wants. |
--profile <name> | Use this profile, as PENSTOCK_PROFILE would. |
Example
$ penstock trust --list
Needs: Free
| Exit code | Meaning |
|---|---|
0 | The project is trusted, forgotten, or the list was printed. |
1 | There is no penstock.yaml there to trust, or the path is not there. |
2 | The 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.
| Option | What it does |
|---|---|
--config | Print 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 code | Meaning |
|---|---|
0 | The server stopped, or the client entry was printed. |
1 | A tool was asked for something this machine does not have. |
2 | The 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.
| Option | What it does |
|---|---|
--refresh | Check GitHub for a newer release now. |
--profile <name> | Use this profile, as PENSTOCK_PROFILE would. |
Example
$ penstock connectors --refresh
Needs: Free
| Exit code | Meaning |
|---|---|
0 | The list was printed. |
1 | The release list could not be downloaded, and the answer says when it will try again. |
2 | The 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.
| Option | What it does |
|---|---|
--refresh | Check GitHub for a newer release first. |
--profile <name> | Use this profile, as PENSTOCK_PROFILE would. |
Example
$ penstock connectors resolve http-json
Needs: Free
| Exit code | Meaning |
|---|---|
0 | The template was printed. |
1 | There is no template with that id, and the answer says what there is. |
2 | The 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.
| Option | What 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 code | Meaning |
|---|---|
0 | The wrapper was moved. |
1 | It could not be, and the answer says which step refused. |
2 | The CLI itself failed, not the command; the message says what. |
What you see
- Most commands print one line for each file. A line starts with a word:
ok,fail,wrote,kept,same,would changeorsent. - Findings are indented under their file.
- The exit code says the outcome. See the exit codes above.
Common problems
- An option is ignored.
- Check that it belongs to that command. For example
--writeis forlayoutandfmt, and--waitfordeploy.penstock <command> --helplists 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 licenseto 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 trustin it, or pass--trust-projectfor one run. - The engine refuses a run as not a test engine.
- Set
deployment.forTests: trueon 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>andreportcompare against Git history. Run them inside a checkout.