From bpmn-to-code
Bootstraps a Spring Boot service project from a BPMN file with hexagonal architecture. Generates workers, use cases, and services wired to ProcessApi constants.
How this skill is triggered — by the user, by Claude, or both
Slash command
/bpmn-to-code:create-process-service <path/to/process.bpmn> [--output-dir <dir>] [--group-id <id>] [--artifact-id <id>] [--build-tool gradle|maven]<path/to/process.bpmn> [--output-dir <dir>] [--group-id <id>] [--artifact-id <id>] [--build-tool gradle|maven]This skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Bootstrap a complete, compilable Spring Boot service project from a `.bpmn` file following
ISSUES.mdresources/buildtool/gradle.mdresources/buildtool/maven.mdresources/compat.mdresources/config/application-camunda7.mdresources/config/application-operaton.mdresources/config/application-zeebe.mdresources/stack/compose-embedded.mdresources/stack/compose-zeebe.mdresources/templates/code.mdresources/templates/readme.mdBootstrap a complete, compilable Spring Boot service project from a .bpmn file following
hexagonal architecture. Every layer is generated — workers (or delegates), use case interfaces,
service stubs, and outbound ports — all referencing bpmn-to-code ProcessApi constants.
No raw BPMN strings anywhere in the generated code.
Templates and configuration snippets for each engine, build tool, and approach live in the
resources/ directory alongside this skill. Read those files on demand; do not rely on memory.
Reference examples:
emaarco/engine-safari): plain delegate pattern (service/camunda-7),
process-engine-api worker pattern (service/camunda-7-with-process-engine-api)emaarco/easy-zeebe): full Zeebe Docker Composeapplication-local.yaml H2 patternAskUserQuestion whenever a required value is missing or a decision needs user input.| Engine | process-engine-api adapter? | Default approach | Worker type |
|---|---|---|---|
| CAMUNDA_7 | ✅ c7-embedded | process-engine-api | @ProcessEngineWorker |
| ZEEBE | ✅ c8 | process-engine-api | @ProcessEngineWorker |
| OPERATON | ❌ none available | plain JavaDelegate | BaseDelegate |
For CAMUNDA_7/ZEEBE: offer process-engine-api as default, allow opt-out. For OPERATON: always use plain — explain why in the summary.
Parse $ARGUMENTS. For missing required values use AskUserQuestion.
| Parameter | Required | Default |
|---|---|---|
bpmnFile | yes | — |
outputDir | no | ./ |
groupId | yes | — |
artifactId | yes | — |
packageName | no | ${groupId}.${artifactId} (hyphens → dots) |
buildTool | no | GRADLE |
engine | no | auto-detect (see below) |
useProcessEngineApi | no | true for CAMUNDA_7/ZEEBE, false for OPERATON |
outputLanguage | no | KOTLIN |
Engine auto-detection: Read the BPMN file and scan the XML:
xmlns:zeebe or <zeebe:taskDefinition → ZEEBEcamunda:class=, camunda:expression=, or camunda:type= (no zeebe namespace) → CAMUNDA_7xmlns:operaton or operaton:class= → OPERATONJava version detection:
java -version 2>&1 | head -1
Extract the major version (e.g. 21). Check resources/compat.md for the engine's minimum
Java requirement. If no Java is found, or the detected version is below the engine minimum,
use AskUserQuestion to let the user confirm or specify the Java version to target.
Spring Boot version:
Fetch the current default from Spring Initializr:
curl -s "https://start.spring.io/actuator/info" \
| grep -o '"default":"[^"]*"' | head -1 | grep -o '[0-9][^"]*'
Use the returned value as bootVersion. Check resources/compat.md for engine-specific
Spring Boot minimums and warn if the fetched version is below them.
If the fetch fails, fall back to 3.5.0 (the minimum version accepted by start.spring.io).
Kotlin version (GRADLE + KOTLIN only):
Read resources/compat.md for the minimum Kotlin plugin version required by the chosen
engine BOM. Record this as kotlinVersion — you will patch it into the build file in Step 3.
Do NOT use the Kotlin version generated by Initializr as-is; always check compatibility.
Show all detected/defaulted values and ask for confirmation before proceeding.
curl -G "https://start.spring.io/starter.zip" \
-d type={{gradle-project-kotlin|maven-project}} \
-d language={{kotlin|java}} \
-d bootVersion={{bootVersion}} \
-d groupId={{groupId}} \
-d artifactId={{artifactId}} \
-d name={{artifactId}} \
-d packageName={{packageName}} \
-d dependencies=web,actuator \
-o /tmp/{{artifactId}}-scaffold.zip
unzip /tmp/{{artifactId}}-scaffold.zip -d "{{outputDir}}/{{artifactId}}"
type: gradle-project-kotlin for GRADLE (Kotlin DSL), maven-project for MAVENlanguage: kotlin for KOTLIN, java for JAVAAll subsequent operations target {{outputDir}}/{{artifactId}}.
Read the relevant resource file for the chosen build tool:
resources/buildtool/gradle.mdresources/buildtool/maven.mdFrom that file, take the sections matching the chosen engine and approach. Look up the latest bpmn-to-code plugin/artifact version via WebFetch if needed.
For Gradle + Kotlin: also patch the Kotlin version in the plugins {} block to
{{kotlinVersion}} (both the kotlin("jvm") and kotlin("plugin.spring") lines).
For Gradle: also patch the Java toolchain languageVersion to the detected {{javaVersion}}.
Show a diff-style summary of all planned changes and ask:
"Apply these changes to build.gradle.kts / pom.xml? (yes / edit / cancel)"
Apply only after confirmation.
Docker Compose + .env
Read the relevant stack resource:
resources/stack/compose-embedded.mdresources/stack/compose-zeebe.mdWrite stack/compose.yaml and stack/.env verbatim, substituting {{artifactId}}.
Spring application config
Read the relevant config resource:
resources/config/application-camunda7.mdresources/config/application-operaton.mdresources/config/application-zeebe.mdWrite src/main/resources/application.yml and src/main/resources/application-local.yml
verbatim, substituting {{artifactId}}.
No confirmation prompt needed for config files — they are straightforward and reversible.
mkdir -p "{{outputDir}}/{{artifactId}}/src/main/resources/bpmn"
cp "{{bpmnFile}}" "{{outputDir}}/{{artifactId}}/src/main/resources/bpmn/"
Run codegen from inside the project directory:
./gradlew generateBpmnModelApimvn io.miragon:bpmn-to-code-maven:generate-bpmn-apiIf codegen fails, show the full error and stop. Do not proceed until it succeeds.
Once complete, locate and read the generated *ProcessApi.kt (or .java) file under
src/main/kotlin/{{packagePath}}/api/ (or src/main/java/...). Extract:
ProcessApi class/object name (e.g. NewsletterSubscriptionProcessApi)
PROCESS_ID constant value → derive {{processId}} and {{ProcessName}}
PROCESS_ENGINE constant → confirm it matches the selected engine
All ServiceTasks entries → drive worker/delegate generation (name + value)
Variables structure — Variables contains only per-element nested objects; there are
no top-level flat constants. Each nested object is named in PascalCase from the BPMN element ID
and holds the variables scoped to that specific flow node:
object Variables {
object ActivitySendWelcomeMail {
const val SUBSCRIPTION_ID: String = "subscriptionId"
}
object ActivitySendConfirmationMail {
const val SUBSCRIPTION_ID: String = "subscriptionId"
const val TEST_VARIABLE: String = "testVariable"
}
}
For each TaskType, find the matching service task element name (from Elements) and look up
Variables.{{ElementName}} to determine which variables to inject into that worker/delegate.
Read resources/templates/code.md. Use the section matching the chosen approach:
For each TaskType constant, derive names and generate the files described in the template.
Create the domain/.gitkeep placeholder regardless of approach.
Variable injection (per element): When generating each worker/delegate, look up the
per-element Variables sub-object for that service task (e.g. Variables.ActivitySendWelcomeMail).
Use those constants — not the top-level flat list — to generate @Variable parameters or
execution.getVariable(...) calls. If no per-element sub-object exists, leave the method
parameterless.
Read resources/templates/readme.md. Substitute all {{placeholder}} markers with the
actual values collected and derived during the preceding steps.
For {{taskTypesTable}}, generate one Markdown table row per TaskType:
| `{{ProcessApiClass}}.ServiceTasks.{{TASK_CONST}}` | `{{taskTypeValue}}` |
Include or omit conditional blocks (<!-- Include ... -->) according to engine and approach.
Write the result to README.md in the project root.
Run from the project directory:
./gradlew build./gradlew build -x test
(ZEEBE requires a running broker for Spring context tests — skip tests during scaffolding)mvn verifymvn verify -DskipTestsIf the build fails: show the full error, diagnose the most likely cause, attempt a fix,
and re-run. Use AskUserQuestion if the fix requires information you don't have.
Do not mark the task complete until the build passes.
Report:
✅ Project scaffolded: {{outputDir}}/{{artifactId}}/
Build tool: {{buildTool}}
Engine: {{engine}}
Approach: {{approach}}
Java: {{javaVersion}}
Spring Boot: {{bootVersion}}
Tasks: {{count}} workers/delegates generated
{{list of TASK_CONST names}}
── Quick start (embedded engines — H2, no Docker) ─────────────────────────
[Gradle] SPRING_PROFILES_ACTIVE=local ./gradlew bootRun
[Maven] mvn spring-boot:run -Dspring-boot.run.profiles=local
── Full stack (Docker) ────────────────────────────────────────────────────
cd {{outputDir}}/{{artifactId}}/stack && docker compose up -d
[Gradle] ./gradlew bootRun
[Maven] mvn spring-boot:run
For ZEEBE, replace the quick-start block with:
⚠️ Zeebe requires Docker — no H2 shortcut available.
cd stack && docker compose up -d # then bootRun / spring-boot:run
Zeebe UI: http://localhost:8080 (demo / demo)
⚠️ contextLoads() test skipped (needs running Zeebe). Run ./gradlew test after docker compose up -d.
For OPERATON plain delegates, append:
⚠️ Update BPMN service tasks: set camunda:class / operaton:class to each
delegate's fully-qualified class name before deploying.
Suggest next steps:
Service stubs./migrate-to-bpmn-to-code-apis when adding more BPMN files later./setup-bpmn-to-code-gradle or /setup-bpmn-to-code-maven to adjust codegen config.Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub miragon/bpmn-to-code --plugin bpmn-to-code