Guide / Share and move
Edit a diagram from the command line
Change a diagram from a script or an AI assistant with the editor's own modelling, and have Penstock refuse an edit that would leave the drawing broken.
What it is
A diagram is XML with coordinates. Editing it by hand or by a text tool gets the process right and the picture wrong: the new task sits on top of another, the flow runs through a shape, a boundary event floats off its task. penstock edit does not touch the XML. It opens the diagram in the same modelling engine the editor uses, makes the change the way a person would on the canvas, and saves the result. A new element is placed after the one you name and connected to it, the way the editor places one.
The same operations are available to an AI assistant as the MCP tool edit_diagram. See The MCP server. Both run the same code.
Read before you edit
Edits refer to ids. penstock show lists every element and flow of a diagram with its type, id, name and connections, and penstock show --format json adds the parent and the engine properties. The MCP tool show_diagram returns the same. The engine is the one in penstock.yaml, or the one the diagram itself uses.
$ penstock show src/orders/order.bpmn
src/orders/order.bpmn (camunda-8)
TYPE ID NAME FLOW
StartEvent StartEvent_1 Order received
ServiceTask Task_check Check stock
...
The four operations
| Operation | What it does | Needs |
|---|---|---|
add | Adds an element after an existing one, placed and connected as the editor does it. It can be given a name, an id, an event type and properties. | element and after |
connect | Draws a sequence flow between two elements, with an optional name and condition. | from and to |
set | Changes properties of an element or a flow. | id and properties |
remove | Removes an element or a flow, with the flows that were attached to it. | id |
On the command line, one operation is spelled out, and several come as JSON:
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
penstock edit orders.bpmn --ops-file changes.json
echo '[{"op":"remove","id":"Task_legacy"}]' | penstock edit orders.bpmn --ops-file -
Several operations in one call apply together or not at all. If the third one fails, nothing is written, and the message says which one it was.
What can be added
- Elements:
task,user-task,service-task,script-task,send-task,receive-task,business-rule-task,manual-task,call-activity,sub-process,end-event,intermediate-catch-event,intermediate-throw-event,exclusive-gateway,parallel-gateway,inclusive-gatewayandevent-based-gateway. - Event types, for the event elements, in
event:message,timer,signal,error,escalation,conditionalandterminate. - Not after an end event, after a flow, or after a pool or a lane itself: name an element inside the lane.
- The id of a new element is the one you give, or else the one your conventions in
penstock.yamlmake. The conventions' defaults for that element type are applied to it.
Which properties
Only properties the engine of the diagram knows are accepted. Anything else is refused with the list of the ones that are. penstock edit <file> --schema prints the exact contract for the file's engine as a JSON schema, and changes nothing. It is free.
| Engine | Properties |
|---|---|
| All | name, documentation and, on a sequence flow, condition |
| Camunda 8 | On service, send, business rule and script tasks: jobType, retries. On a user task: assignee, candidateUsers, candidateGroups, formId. On a call activity: processId. On tasks: inputs and outputs as mappings. The condition is a FEEL expression. |
| Camunda 7, Operaton, CIB seven | On service, send, business rule and script tasks: class, delegateExpression, expression, resultVariable, topic. On any element: asyncBefore, asyncAfter. On a user task: assignee, candidateUsers, candidateGroups, formKey. On a call activity: calledElement. On tasks: inputs and outputs. |
| Classic BPMN | The common ones only |
With --set name=value, the value of a boolean property is true or false, and of a number such as retries digits. --input name=value and --output name=value add a mapping.
What stops an edit
After the edit Penstock saves the diagram in memory, reads it back, and runs the drawing check on both versions. It compares them, so a problem that was already there is not blamed on the edit.
- If the diagram no longer reads back as well as before, nothing is written.
- If the edit adds one of these drawing problems, it is refused:
LOST-CONNECTION,SEMANTIC-DRIFT,BACKTRACK,CONTAINMENT,OVERLAP,EDGE-THROUGH-NODE,DETACHED-EDGE,LABEL-OVERLAP,DANGLING-CONNECTIONorBOUNDARY-OFF-HOST. These break the picture. See Diagram check. - If it only adds readability problems, it is written, and the answer lists them after note reads less well.
The answer always says what was done, lists what changed by element, and says whether anything is broken. --dry-run does all of it and writes nothing. --force writes even when the edit would break the drawing.
$ penstock edit src/orders/order.bpmn add service-task --after Task_check --name "Reserve stock" --dry-run
added ServiceTask Activity_1q2w3e4 after Task_check
added ServiceTask "Reserve stock" Activity_1q2w3e4
ok nothing broken
Dry run: src/orders/order.bpmn was not changed.
Limits
- BPMN diagrams only. A decision or a form is not edited this way.
- The root of the diagram, a process or a collaboration, cannot be removed.
- It does not move shapes. A new element goes where the editor would put it, and the rest stays.
- It is part of Pro, and Team in a pipeline. The MCP tool needs the same plan. A pipeline that has no Team key is told so, and nothing is changed.
- The conventions of the project apply, so the same edit made in two projects can give different ids.
What you see
- A list of what was applied, the changes by element, and a line that says ok nothing broken or fail breaks the drawing with the count of new problems.
- A changed diagram, which you review as a normal diff. See Diagram diff.
Common problems
- "Nothing was written".
- An operation could not be applied, or the result would not read back. The message names the operation by its number.
- The edit is refused as breaking the drawing.
- The answer lists the new problems. Change the edit, for example add after another element, or use
--forceif you will tidy it by hand. - A property is refused.
- The engine of the diagram does not have it. Run
penstock edit <file> --schemato see the ones it has.