@bpmnkit/cli 0.0.27 → 0.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,679 @@
1
+ import { readFile, writeFile } from "node:fs/promises";
2
+ import { Bpmn, compactify, expand } from "@bpmnkit/core";
3
+ // ── JSON schema reference ─────────────────────────────────────────────────────
4
+ const SCHEMA_HELP = `CompactDiagram JSON schema — for --definition and stdin input
5
+ ==============================================================
6
+
7
+ TOP-LEVEL
8
+ { "id": "Definitions_<unique>", "processes": [CompactProcess] }
9
+
10
+ CompactProcess
11
+ { "id": "proc-id", "name"?: "Human Name",
12
+ "elements": [CompactElement], "flows": [CompactFlow] }
13
+
14
+ CompactElement — all fields except id+type are optional
15
+ {
16
+ "id": unique element ID,
17
+ "type": BpmnElementType (see below),
18
+ "name": display label,
19
+
20
+ serviceTask/sendTask:
21
+ "jobType": Zeebe job type string (e.g. "my-worker"),
22
+ "taskHeaders": { key: value } — Zeebe task headers,
23
+ "resultVariable": variable to store the response in,
24
+
25
+ callActivity:
26
+ "calledProcess": process ID of the called process,
27
+
28
+ userTask:
29
+ "formId": Camunda form ID,
30
+
31
+ businessRuleTask:
32
+ "decisionId": DMN decision ID,
33
+ "resultVariable": variable to store decision result,
34
+
35
+ events (startEvent, endEvent, boundaryEvent, intermediateCatchEvent, etc.):
36
+ "eventType": timer | message | signal | error | escalation
37
+ | terminate | cancel | conditional | link | compensate,
38
+
39
+ boundaryEvent:
40
+ "attachedTo": ID of the host task,
41
+ "interrupting": false for non-interrupting (default: true),
42
+
43
+ subProcess/eventSubProcess/transaction:
44
+ "children": { "elements": [...], "flows": [...] }
45
+ }
46
+
47
+ CompactFlow
48
+ {
49
+ "id": unique flow ID,
50
+ "from": source element ID,
51
+ "to": target element ID,
52
+ "name"?: flow label,
53
+ "condition"?: FEEL condition expression (e.g. "= status = \\"approved\\"")
54
+ }
55
+
56
+ ELEMENT TYPES
57
+ Tasks: serviceTask userTask scriptTask businessRuleTask
58
+ callActivity sendTask receiveTask manualTask task
59
+ Events: startEvent endEvent intermediateCatchEvent intermediateThrowEvent
60
+ boundaryEvent
61
+ Gateways: exclusiveGateway parallelGateway inclusiveGateway
62
+ eventBasedGateway complexGateway
63
+ Containers: subProcess adHocSubProcess eventSubProcess transaction
64
+
65
+ HTTP CONNECTOR (serviceTask)
66
+ { "type": "serviceTask", "jobType": "io.camunda:http-json:1",
67
+ "taskHeaders": { "url": "https://...", "method": "POST" },
68
+ "resultVariable": "response" }
69
+
70
+ PATCH FORMAT (for --patch and stdin when --input is set)
71
+ Adds elements and flows to an existing process. IDs must not collide.
72
+ {
73
+ "elements": [CompactElement, ...], // new elements to add
74
+ "flows": [CompactFlow, ...] // new flows to add (can reference existing IDs)
75
+ }
76
+
77
+ Example — add a rejection path to an existing gateway named "gw":
78
+ {
79
+ "elements": [
80
+ { "id": "rejected", "type": "serviceTask", "name": "Notify Rejection", "jobType": "notify" },
81
+ { "id": "end-reject","type": "endEvent", "name": "Rejected" }
82
+ ],
83
+ "flows": [
84
+ { "id": "f-rej1", "from": "gw", "to": "rejected", "condition": "= not approved", "name": "No" },
85
+ { "id": "f-rej2", "from": "rejected", "to": "end-reject" }
86
+ ]
87
+ }
88
+
89
+ FULL EXAMPLE — order approval
90
+ {
91
+ "id": "Definitions_order",
92
+ "processes": [{
93
+ "id": "order-approval",
94
+ "name": "Order Approval",
95
+ "elements": [
96
+ { "id": "start", "type": "startEvent", "name": "Order Received" },
97
+ { "id": "validate", "type": "serviceTask", "name": "Validate", "jobType": "validate-order" },
98
+ { "id": "gw", "type": "exclusiveGateway","name": "Valid?" },
99
+ { "id": "review", "type": "userTask", "name": "Manual Review", "formId": "review-form" },
100
+ { "id": "process", "type": "serviceTask", "name": "Process Order", "jobType": "process-order" },
101
+ { "id": "end-ok", "type": "endEvent", "name": "Processed" },
102
+ { "id": "end-rej", "type": "endEvent", "name": "Rejected" },
103
+ { "id": "err", "type": "boundaryEvent", "attachedTo": "validate", "eventType": "error" },
104
+ { "id": "end-err", "type": "endEvent", "name": "Validation Error", "eventType": "error" }
105
+ ],
106
+ "flows": [
107
+ { "id": "f1", "from": "start", "to": "validate" },
108
+ { "id": "f2", "from": "validate", "to": "gw" },
109
+ { "id": "f3", "from": "gw", "to": "review", "condition": "= not valid", "name": "Invalid" },
110
+ { "id": "f4", "from": "gw", "to": "process", "condition": "= valid", "name": "Valid" },
111
+ { "id": "f5", "from": "review", "to": "end-rej", "name": "Rejected" },
112
+ { "id": "f6", "from": "review", "to": "process", "name": "Approved" },
113
+ { "id": "f7", "from": "process", "to": "end-ok" },
114
+ { "id": "f8", "from": "err", "to": "end-err" }
115
+ ]
116
+ }]
117
+ }`;
118
+ const TEMPLATES = {
119
+ empty: (id, name) => ({
120
+ id: `Definitions_${id}`,
121
+ processes: [
122
+ {
123
+ id,
124
+ name: name ?? "New Process",
125
+ elements: [{ id: "start", type: "startEvent", name: "Start" }],
126
+ flows: [],
127
+ },
128
+ ],
129
+ }),
130
+ minimal: (id, name) => ({
131
+ id: `Definitions_${id}`,
132
+ processes: [
133
+ {
134
+ id,
135
+ name: name ?? "New Process",
136
+ elements: [
137
+ { id: "start", type: "startEvent", name: "Start" },
138
+ {
139
+ id: "task1",
140
+ type: "serviceTask",
141
+ name: name ? `${name} Task` : "Process Request",
142
+ jobType: "my-worker",
143
+ },
144
+ { id: "end", type: "endEvent", name: "End" },
145
+ ],
146
+ flows: [
147
+ { id: "f1", from: "start", to: "task1" },
148
+ { id: "f2", from: "task1", to: "end" },
149
+ ],
150
+ },
151
+ ],
152
+ }),
153
+ "user-task": (id, name) => ({
154
+ id: `Definitions_${id}`,
155
+ processes: [
156
+ {
157
+ id,
158
+ name: name ?? "Human Task Process",
159
+ elements: [
160
+ { id: "start", type: "startEvent", name: "Start" },
161
+ { id: "ut1", type: "userTask", name: name ?? "Review", formId: "my-form" },
162
+ { id: "end", type: "endEvent", name: "Done" },
163
+ ],
164
+ flows: [
165
+ { id: "f1", from: "start", to: "ut1" },
166
+ { id: "f2", from: "ut1", to: "end" },
167
+ ],
168
+ },
169
+ ],
170
+ }),
171
+ "call-activity": (id, name) => ({
172
+ id: `Definitions_${id}`,
173
+ processes: [
174
+ {
175
+ id,
176
+ name: name ?? "Orchestration Process",
177
+ elements: [
178
+ { id: "start", type: "startEvent", name: "Start" },
179
+ {
180
+ id: "ca1",
181
+ type: "callActivity",
182
+ name: name ?? "Run Sub-Process",
183
+ calledProcess: "child-process",
184
+ },
185
+ { id: "end", type: "endEvent", name: "Done" },
186
+ ],
187
+ flows: [
188
+ { id: "f1", from: "start", to: "ca1" },
189
+ { id: "f2", from: "ca1", to: "end" },
190
+ ],
191
+ },
192
+ ],
193
+ }),
194
+ "business-rule": (id, name) => ({
195
+ id: `Definitions_${id}`,
196
+ processes: [
197
+ {
198
+ id,
199
+ name: name ?? "Decision Process",
200
+ elements: [
201
+ { id: "start", type: "startEvent", name: "Start" },
202
+ {
203
+ id: "brt1",
204
+ type: "businessRuleTask",
205
+ name: name ?? "Evaluate Decision",
206
+ decisionId: "my-decision",
207
+ resultVariable: "decisionResult",
208
+ },
209
+ { id: "end", type: "endEvent", name: "Done" },
210
+ ],
211
+ flows: [
212
+ { id: "f1", from: "start", to: "brt1" },
213
+ { id: "f2", from: "brt1", to: "end" },
214
+ ],
215
+ },
216
+ ],
217
+ }),
218
+ approval: (id, name) => ({
219
+ id: `Definitions_${id}`,
220
+ processes: [
221
+ {
222
+ id,
223
+ name: name ?? "Approval Process",
224
+ elements: [
225
+ { id: "start", type: "startEvent", name: "Request Received" },
226
+ { id: "review", type: "userTask", name: "Review Request", formId: "review-form" },
227
+ { id: "gw", type: "exclusiveGateway", name: "Approved?" },
228
+ {
229
+ id: "process",
230
+ type: "serviceTask",
231
+ name: "Process Approval",
232
+ jobType: "process-approval",
233
+ },
234
+ { id: "end-ok", type: "endEvent", name: "Approved" },
235
+ { id: "end-rej", type: "endEvent", name: "Rejected" },
236
+ ],
237
+ flows: [
238
+ { id: "f1", from: "start", to: "review" },
239
+ { id: "f2", from: "review", to: "gw" },
240
+ { id: "f3", from: "gw", to: "process", condition: "= approved", name: "Yes" },
241
+ { id: "f4", from: "gw", to: "end-rej", condition: "= not approved", name: "No" },
242
+ { id: "f5", from: "process", to: "end-ok" },
243
+ ],
244
+ },
245
+ ],
246
+ }),
247
+ parallel: (id, name) => ({
248
+ id: `Definitions_${id}`,
249
+ processes: [
250
+ {
251
+ id,
252
+ name: name ?? "Parallel Process",
253
+ elements: [
254
+ { id: "start", type: "startEvent", name: "Start" },
255
+ { id: "fork", type: "parallelGateway", name: "Fork" },
256
+ { id: "task1", type: "serviceTask", name: "Task A", jobType: "task-a" },
257
+ { id: "task2", type: "serviceTask", name: "Task B", jobType: "task-b" },
258
+ { id: "join", type: "parallelGateway", name: "Join" },
259
+ { id: "end", type: "endEvent", name: "End" },
260
+ ],
261
+ flows: [
262
+ { id: "f1", from: "start", to: "fork" },
263
+ { id: "f2", from: "fork", to: "task1" },
264
+ { id: "f3", from: "fork", to: "task2" },
265
+ { id: "f4", from: "task1", to: "join" },
266
+ { id: "f5", from: "task2", to: "join" },
267
+ { id: "f6", from: "join", to: "end" },
268
+ ],
269
+ },
270
+ ],
271
+ }),
272
+ inclusive: (id, name) => ({
273
+ id: `Definitions_${id}`,
274
+ processes: [
275
+ {
276
+ id,
277
+ name: name ?? "Inclusive Gateway Process",
278
+ elements: [
279
+ { id: "start", type: "startEvent", name: "Start" },
280
+ { id: "split", type: "inclusiveGateway", name: "Which?" },
281
+ { id: "task1", type: "serviceTask", name: "Option A", jobType: "option-a" },
282
+ { id: "task2", type: "serviceTask", name: "Option B", jobType: "option-b" },
283
+ { id: "merge", type: "inclusiveGateway", name: "Merge" },
284
+ { id: "end", type: "endEvent", name: "End" },
285
+ ],
286
+ flows: [
287
+ { id: "f1", from: "start", to: "split" },
288
+ { id: "f2", from: "split", to: "task1", condition: "= needsA", name: "A" },
289
+ { id: "f3", from: "split", to: "task2", condition: "= needsB", name: "B" },
290
+ { id: "f4", from: "task1", to: "merge" },
291
+ { id: "f5", from: "task2", to: "merge" },
292
+ { id: "f6", from: "merge", to: "end" },
293
+ ],
294
+ },
295
+ ],
296
+ }),
297
+ "timer-start": (id, name) => ({
298
+ id: `Definitions_${id}`,
299
+ processes: [
300
+ {
301
+ id,
302
+ name: name ?? "Scheduled Process",
303
+ elements: [
304
+ { id: "start", type: "startEvent", name: "Timer", eventType: "timer" },
305
+ {
306
+ id: "task1",
307
+ type: "serviceTask",
308
+ name: name ?? "Scheduled Job",
309
+ jobType: "scheduled-worker",
310
+ },
311
+ { id: "end", type: "endEvent", name: "Done" },
312
+ ],
313
+ flows: [
314
+ { id: "f1", from: "start", to: "task1" },
315
+ { id: "f2", from: "task1", to: "end" },
316
+ ],
317
+ },
318
+ ],
319
+ }),
320
+ "message-start": (id, name) => ({
321
+ id: `Definitions_${id}`,
322
+ processes: [
323
+ {
324
+ id,
325
+ name: name ?? "Message-Triggered Process",
326
+ elements: [
327
+ { id: "start", type: "startEvent", name: "Message Received", eventType: "message" },
328
+ {
329
+ id: "task1",
330
+ type: "serviceTask",
331
+ name: name ?? "Handle Message",
332
+ jobType: "message-handler",
333
+ },
334
+ { id: "end", type: "endEvent", name: "Done" },
335
+ ],
336
+ flows: [
337
+ { id: "f1", from: "start", to: "task1" },
338
+ { id: "f2", from: "task1", to: "end" },
339
+ ],
340
+ },
341
+ ],
342
+ }),
343
+ "error-boundary": (id, name) => ({
344
+ id: `Definitions_${id}`,
345
+ processes: [
346
+ {
347
+ id,
348
+ name: name ?? "Error Handling Process",
349
+ elements: [
350
+ { id: "start", type: "startEvent", name: "Start" },
351
+ { id: "task1", type: "serviceTask", name: name ?? "Risky Task", jobType: "risky-worker" },
352
+ {
353
+ id: "err",
354
+ type: "boundaryEvent",
355
+ attachedTo: "task1",
356
+ eventType: "error",
357
+ name: "Error",
358
+ },
359
+ { id: "end-ok", type: "endEvent", name: "Success" },
360
+ { id: "end-err", type: "endEvent", name: "Failed", eventType: "error" },
361
+ ],
362
+ flows: [
363
+ { id: "f1", from: "start", to: "task1" },
364
+ { id: "f2", from: "task1", to: "end-ok" },
365
+ { id: "f3", from: "err", to: "end-err" },
366
+ ],
367
+ },
368
+ ],
369
+ }),
370
+ subprocess: (id, name) => ({
371
+ id: `Definitions_${id}`,
372
+ processes: [
373
+ {
374
+ id,
375
+ name: name ?? "Sub-Process Demo",
376
+ elements: [
377
+ { id: "start", type: "startEvent", name: "Start" },
378
+ {
379
+ id: "sp1",
380
+ type: "subProcess",
381
+ name: name ?? "Sub-Process",
382
+ children: {
383
+ elements: [
384
+ { id: "sp_start", type: "startEvent", name: "Begin" },
385
+ { id: "sp_task", type: "serviceTask", name: "Inner Task", jobType: "inner-worker" },
386
+ { id: "sp_end", type: "endEvent", name: "Finish" },
387
+ ],
388
+ flows: [
389
+ { id: "sf1", from: "sp_start", to: "sp_task" },
390
+ { id: "sf2", from: "sp_task", to: "sp_end" },
391
+ ],
392
+ },
393
+ },
394
+ { id: "end", type: "endEvent", name: "End" },
395
+ ],
396
+ flows: [
397
+ { id: "f1", from: "start", to: "sp1" },
398
+ { id: "f2", from: "sp1", to: "end" },
399
+ ],
400
+ },
401
+ ],
402
+ }),
403
+ "event-subprocess": (id, name) => ({
404
+ id: `Definitions_${id}`,
405
+ processes: [
406
+ {
407
+ id,
408
+ name: name ?? "Process with Error Handler",
409
+ elements: [
410
+ { id: "start", type: "startEvent", name: "Start" },
411
+ { id: "task1", type: "serviceTask", name: name ?? "Main Task", jobType: "main-worker" },
412
+ { id: "end", type: "endEvent", name: "End" },
413
+ {
414
+ id: "evtsp",
415
+ type: "eventSubProcess",
416
+ name: "Error Handler",
417
+ children: {
418
+ elements: [
419
+ {
420
+ id: "evtsp_start",
421
+ type: "startEvent",
422
+ name: "Error",
423
+ eventType: "error",
424
+ interrupting: false,
425
+ },
426
+ {
427
+ id: "evtsp_task",
428
+ type: "serviceTask",
429
+ name: "Compensate",
430
+ jobType: "compensate-worker",
431
+ },
432
+ { id: "evtsp_end", type: "endEvent", name: "Handled" },
433
+ ],
434
+ flows: [
435
+ { id: "sf1", from: "evtsp_start", to: "evtsp_task" },
436
+ { id: "sf2", from: "evtsp_task", to: "evtsp_end" },
437
+ ],
438
+ },
439
+ },
440
+ ],
441
+ flows: [
442
+ { id: "f1", from: "start", to: "task1" },
443
+ { id: "f2", from: "task1", to: "end" },
444
+ ],
445
+ },
446
+ ],
447
+ }),
448
+ };
449
+ const TEMPLATE_NAMES = Object.keys(TEMPLATES).sort();
450
+ // ── Stdin reader ──────────────────────────────────────────────────────────────
451
+ async function readStdin() {
452
+ const chunks = [];
453
+ for await (const chunk of process.stdin) {
454
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
455
+ }
456
+ return Buffer.concat(chunks).toString("utf-8").trim();
457
+ }
458
+ // ── Command ───────────────────────────────────────────────────────────────────
459
+ const generateBpmnCmd = {
460
+ name: "bpmn",
461
+ description: "Generate a BPMN file from parameters, a template, or a CompactDiagram JSON definition",
462
+ args: [],
463
+ flags: [
464
+ {
465
+ name: "process-id",
466
+ short: "i",
467
+ description: "Process ID (used in templates)",
468
+ type: "string",
469
+ },
470
+ {
471
+ name: "name",
472
+ short: "n",
473
+ description: "Process display name (used in templates)",
474
+ type: "string",
475
+ },
476
+ {
477
+ name: "output",
478
+ short: "o",
479
+ description: "Output file path. Default: <process-id>.bpmn. Use - for stdout.",
480
+ type: "string",
481
+ },
482
+ {
483
+ name: "template",
484
+ description: `Template name. Options: ${TEMPLATE_NAMES.join(", ")}`,
485
+ type: "string",
486
+ default: "minimal",
487
+ enum: TEMPLATE_NAMES,
488
+ },
489
+ {
490
+ name: "definition",
491
+ short: "d",
492
+ description: "CompactDiagram JSON string (overrides --template). See --help-schema.",
493
+ type: "string",
494
+ },
495
+ {
496
+ name: "help-schema",
497
+ description: "Print the CompactDiagram JSON schema and exit",
498
+ type: "boolean",
499
+ },
500
+ {
501
+ name: "input",
502
+ short: "f",
503
+ description: "Existing .bpmn file to load and modify",
504
+ type: "string",
505
+ },
506
+ {
507
+ name: "patch",
508
+ description: 'JSON patch to apply to --input: {"elements":[...],"flows":[...]}. See --help-schema.',
509
+ type: "string",
510
+ },
511
+ {
512
+ name: "dump-compact",
513
+ description: "Print the CompactDiagram JSON of --input and exit (for AI inspection of existing files)",
514
+ type: "boolean",
515
+ },
516
+ ],
517
+ examples: [
518
+ {
519
+ description: "Minimal service-task process",
520
+ command: "casen generate bpmn --process-id order --name 'Order Processing'",
521
+ },
522
+ {
523
+ description: "Approval workflow template",
524
+ command: "casen generate bpmn --template approval --process-id approve",
525
+ },
526
+ {
527
+ description: "Timer-triggered scheduled job",
528
+ command: "casen generate bpmn --template timer-start --process-id nightly-sync",
529
+ },
530
+ {
531
+ description: "Parallel branch pattern",
532
+ command: "casen generate bpmn --template parallel --process-id enrich --name Enrichment",
533
+ },
534
+ {
535
+ description: "Error boundary with error end event",
536
+ command: "casen generate bpmn --template error-boundary --process-id resilient",
537
+ },
538
+ {
539
+ description: "Full custom definition (AI/scripting path)",
540
+ command: 'casen generate bpmn --definition \'{"id":"Defs","processes":[{"id":"p","elements":[...],"flows":[...]}]}\'',
541
+ },
542
+ {
543
+ description: "Pipe CompactDiagram JSON from AI output",
544
+ command: "echo '{...}' | casen generate bpmn --output my-process.bpmn",
545
+ },
546
+ {
547
+ description: "Print full JSON schema reference",
548
+ command: "casen generate bpmn --help-schema",
549
+ },
550
+ {
551
+ description: "Inspect an existing file as compact JSON (AI planning step)",
552
+ command: "casen generate bpmn --input existing.bpmn --dump-compact",
553
+ },
554
+ {
555
+ description: "Add a new gateway path to an existing file",
556
+ command: 'casen generate bpmn --input order.bpmn --patch \'{"elements":[{"id":"notify","type":"serviceTask","name":"Notify","jobType":"notify-worker"},{"id":"end2","type":"endEvent","name":"Notified"}],"flows":[{"id":"fn1","from":"gw","to":"notify","condition":"= urgent"},{"id":"fn2","from":"notify","to":"end2"}]}\'',
557
+ },
558
+ {
559
+ description: "Pipe a patch from AI output",
560
+ command: 'echo \'{"elements":[...],"flows":[...]}\' | casen generate bpmn --input order.bpmn',
561
+ },
562
+ {
563
+ description: "Re-apply auto-layout to an existing file",
564
+ command: "casen generate bpmn --input messy.bpmn --output clean.bpmn",
565
+ },
566
+ ],
567
+ async run(ctx) {
568
+ // --help-schema: print schema and exit
569
+ if (ctx.flags["help-schema"]) {
570
+ process.stdout.write(`${SCHEMA_HELP}\n`);
571
+ return;
572
+ }
573
+ const processId = ctx.flags["process-id"]?.trim() || "process";
574
+ const name = ctx.flags.name?.trim();
575
+ const outputFlag = ctx.flags.output;
576
+ const defFlag = ctx.flags.definition;
577
+ const inputFile = ctx.flags.input;
578
+ const patchFlag = ctx.flags.patch;
579
+ // ── Modify-existing mode (--input) ─────────────────────────────────────
580
+ if (inputFile) {
581
+ const xml = await readFile(inputFile, "utf-8");
582
+ const defs = Bpmn.parse(xml);
583
+ const compact = compactify(defs);
584
+ // --dump-compact: print JSON for AI inspection and exit
585
+ if (ctx.flags["dump-compact"]) {
586
+ process.stdout.write(`${JSON.stringify(compact, null, 2)}\n`);
587
+ return;
588
+ }
589
+ // Resolve patch from --patch flag or stdin
590
+ let patch = null;
591
+ if (patchFlag) {
592
+ try {
593
+ patch = JSON.parse(patchFlag);
594
+ }
595
+ catch {
596
+ throw new Error("--patch is not valid JSON. Run --help-schema to see the format.");
597
+ }
598
+ }
599
+ else if (!process.stdin.isTTY) {
600
+ const raw = await readStdin();
601
+ if (raw) {
602
+ try {
603
+ patch = JSON.parse(raw);
604
+ }
605
+ catch {
606
+ throw new Error("stdin is not valid patch JSON. Expected {elements:[...],flows:[...]}. Run --help-schema.");
607
+ }
608
+ }
609
+ }
610
+ // Apply patch to first process (covers all single-process cases)
611
+ if (patch) {
612
+ const proc = compact.processes[0];
613
+ if (!proc)
614
+ throw new Error("Input BPMN has no processes");
615
+ if (patch.elements?.length)
616
+ proc.elements.push(...patch.elements);
617
+ if (patch.flows?.length)
618
+ proc.flows.push(...patch.flows);
619
+ }
620
+ const patched = Bpmn.export(expand(compact));
621
+ if (outputFlag === "-") {
622
+ process.stdout.write(patched);
623
+ return;
624
+ }
625
+ const outputPath = typeof outputFlag === "string" && outputFlag.length > 0 ? outputFlag : inputFile;
626
+ await writeFile(outputPath, patched, "utf-8");
627
+ ctx.output.ok(patch ? `Patched and written to ${outputPath}` : `Re-laid-out and written to ${outputPath}`);
628
+ return;
629
+ }
630
+ // ── Generate mode (template / definition / stdin) ──────────────────────
631
+ let compact = null;
632
+ if (defFlag) {
633
+ try {
634
+ compact = JSON.parse(defFlag);
635
+ }
636
+ catch {
637
+ throw new Error("--definition is not valid JSON. Run --help-schema to see the format.");
638
+ }
639
+ }
640
+ else if (!process.stdin.isTTY) {
641
+ const raw = await readStdin();
642
+ if (raw) {
643
+ try {
644
+ compact = JSON.parse(raw);
645
+ }
646
+ catch {
647
+ throw new Error("stdin is not valid CompactDiagram JSON. Run --help-schema to see the format.");
648
+ }
649
+ }
650
+ }
651
+ let xml;
652
+ if (compact) {
653
+ xml = Bpmn.export(expand(compact));
654
+ }
655
+ else {
656
+ const templateName = ctx.flags.template ?? "minimal";
657
+ const templateFn = TEMPLATES[templateName];
658
+ if (!templateFn) {
659
+ throw new Error(`Unknown template "${templateName}". Available: ${TEMPLATE_NAMES.join(", ")}`);
660
+ }
661
+ xml = Bpmn.export(expand(templateFn(processId, name)));
662
+ }
663
+ if (outputFlag === "-") {
664
+ process.stdout.write(xml);
665
+ return;
666
+ }
667
+ const effectiveId = compact?.processes[0]?.id ?? ctx.flags["process-id"] ?? "process";
668
+ const outputPath = typeof outputFlag === "string" && outputFlag.length > 0 ? outputFlag : `${effectiveId}.bpmn`;
669
+ await writeFile(outputPath, xml, "utf-8");
670
+ ctx.output.ok(`BPMN written to ${outputPath}`);
671
+ },
672
+ };
673
+ export const generateGroup = {
674
+ name: "generate",
675
+ aliases: ["gen"],
676
+ description: "Generate BPMN, DMN, and form files from parameters",
677
+ commands: [generateBpmnCmd],
678
+ };
679
+ //# sourceMappingURL=generate.js.map