@eventmodelers/cli 0.0.39 → 1.0.0
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.
- package/README.md +116 -69
- package/cli.js +98 -32
- package/package.json +4 -2
- package/shared/build-kit/lib/adapters/pocketbase-realtime-adapter.js +29 -0
- package/shared/build-kit/lib/adapters/realtime-adapter.js +27 -0
- package/shared/build-kit/lib/adapters/supabase-realtime-adapter.js +24 -0
- package/shared/build-kit/lib/ralph.js +30 -31
- package/shared/build-kit/package.json +3 -1
- package/shared/build-kit/ralph-claude.js +63 -5
- package/shared/skills/connect/SKILL.md +52 -5
- package/shared/skills/learn-eventmodelers-api/SKILL.md +86 -3
- package/shared/skills/load-slice/SKILL.md +16 -0
- package/shared/skills/update-slice-status/SKILL.md +15 -5
- package/stacks/modeling-kit/templates/.claude/skills/add-next-slice/SKILL.md +86 -0
- package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/SKILL.md +28 -0
- package/stacks/modeling-kit/templates/.claude/skills/attributes/SKILL.md +46 -4
- package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/SKILL.md +77 -45
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/SKILL.md +99 -13
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/SKILL.md +21 -1
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +29 -3
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/SKILL.md +71 -11
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md +65 -8
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/SKILL.md +28 -4
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/SKILL.md +8 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/SKILL.md +36 -2
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/SKILL.md +113 -26
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/SKILL.md +14 -2
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/SKILL.md +12 -1
- package/stacks/modeling-kit/templates/.claude/skills/examples/SKILL.md +44 -6
- package/stacks/modeling-kit/templates/.claude/skills/handle-comment/SKILL.md +27 -0
- package/stacks/modeling-kit/templates/.claude/skills/html-screen/SKILL.md +50 -5
- package/stacks/modeling-kit/templates/.claude/skills/place-element/SKILL.md +171 -5
- package/stacks/modeling-kit/templates/.claude/skills/storyboard/SKILL.md +110 -10
- package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/SKILL.md +36 -3
- package/stacks/modeling-kit/templates/.claude/skills/timeline/SKILL.md +110 -11
- package/stacks/modeling-kit/templates/.claude/skills/update-prompt-status/SKILL.md +9 -0
- package/stacks/modeling-kit/templates/.claude/skills/wdyt/SKILL.md +23 -4
- package/stacks/modeling-kit/templates/kit/CLAUDE.md +7 -3
- package/stacks/node/templates/.claude/skills/build-state-view/SKILL.md +13 -7
- package/stacks/supabase/templates/.claude/skills/build-state-view/SKILL.md +13 -7
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/SKILL.md
CHANGED
|
@@ -11,6 +11,8 @@ allowed-tools:
|
|
|
11
11
|
|
|
12
12
|
> **Before doing anything else**, invoke the `connect` skill to resolve `TOKEN`, `BOARD_ID`, `ORG_ID`, and `BASE_URL`. Then invoke the `learn-eventmodelers-api` skill to load the full API reference. Do not proceed until both skills have been loaded.
|
|
13
13
|
|
|
14
|
+
Prefer `mcp__eventmodelers__*` tools when available (registered by the `connect` skill) — the curl blocks below are the fallback for sessions without MCP connected.
|
|
15
|
+
|
|
14
16
|
## Interview Phase (Optional)
|
|
15
17
|
|
|
16
18
|
**When to Interview**: Skip if the user has already identified UI actions/commands and processor triggers. Interview when it's unclear which actions are user-initiated vs. processor-automated.
|
|
@@ -342,11 +344,11 @@ If an event field has **no** corresponding command field and **no** derivation r
|
|
|
342
344
|
"type": "COMMAND",
|
|
343
345
|
"title": "ReserveBike",
|
|
344
346
|
"fields": [
|
|
345
|
-
{"name": "customerId", "type": "String",
|
|
346
|
-
{"name": "bikeId", "type": "String",
|
|
347
|
-
{"name": "stationId", "type": "String",
|
|
348
|
-
{"name": "startTime", "type": "
|
|
349
|
-
{"name": "endTime", "type": "
|
|
347
|
+
{"name": "customerId", "type": "String", "example": "cust-42", "mapping": "session:customerId", "generated": false},
|
|
348
|
+
{"name": "bikeId", "type": "String", "example": "bike-17", "mapping": "user-input", "generated": false},
|
|
349
|
+
{"name": "stationId", "type": "String", "example": "stn-03", "mapping": "user-input", "generated": false},
|
|
350
|
+
{"name": "startTime", "type": "DateTime", "example": "2026-06-01T09:00:00Z", "mapping": "user-input", "generated": false},
|
|
351
|
+
{"name": "endTime", "type": "DateTime", "example": "2026-06-01T17:00:00Z", "mapping": "user-input", "generated": false}
|
|
350
352
|
]
|
|
351
353
|
}
|
|
352
354
|
```
|
|
@@ -366,7 +368,41 @@ Commands go in the `interaction` lane — same column as their resulting event.
|
|
|
366
368
|
|
|
367
369
|
**Every `node:created` call MUST include `cellId`.** Without it the node has no cell reference and will appear stranded at position 0,0 — not in any timeline column.
|
|
368
370
|
|
|
369
|
-
Commands go in the **interaction lane**, same column as the event they produce.
|
|
371
|
+
Commands go in the **interaction lane**, same column as the event they produce.
|
|
372
|
+
|
|
373
|
+
**Prefer MCP** — `place_element` collapses the entire find-row → find/append-column → compute-cellId → create-node → auto-connect sequence (Steps A–D below) into one call:
|
|
374
|
+
```
|
|
375
|
+
mcp__eventmodelers__place_element {
|
|
376
|
+
"boardId": "<BOARD_ID>",
|
|
377
|
+
"timelineId": "<CHAPTER_ID>",
|
|
378
|
+
"elementType": "COMMAND",
|
|
379
|
+
"title": "ReserveBike"
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
`place_element` finds/creates the empty cell in the interaction lane in the correct column and places the node — but it does not accept `fields`. Immediately follow up with `submit_node_events` (`node:changed`) to set `meta.fields` (with `mapping`/`generated`/`cardinality` per the rules above) on the node it returned:
|
|
383
|
+
```
|
|
384
|
+
mcp__eventmodelers__submit_node_events {
|
|
385
|
+
"boardId": "<BOARD_ID>",
|
|
386
|
+
"events": [{
|
|
387
|
+
"id": "<event-uuid>",
|
|
388
|
+
"eventType": "node:changed",
|
|
389
|
+
"nodeId": "<returned-node-id>",
|
|
390
|
+
"boardId": "<BOARD_ID>",
|
|
391
|
+
"timestamp": 1234567890,
|
|
392
|
+
"meta": {
|
|
393
|
+
"type": "COMMAND",
|
|
394
|
+
"title": "ReserveBike",
|
|
395
|
+
"fields": [
|
|
396
|
+
{"name": "customerId", "type": "String", "example": "cust-42", "mapping": "session:customerId"},
|
|
397
|
+
{"name": "bikeId", "type": "String", "example": "bike-17", "mapping": "user-input"},
|
|
398
|
+
{"name": "startTime", "type": "DateTime", "example": "2026-06-01T09:00:00Z","mapping": "user-input"}
|
|
399
|
+
]
|
|
400
|
+
}
|
|
401
|
+
}]
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
**Fallback (no MCP)** — the full manual sequence `place_element` replaces. Before creating each command:
|
|
370
406
|
|
|
371
407
|
**Step A — Find the event's column ID.** Query the event node to read its current cell:
|
|
372
408
|
```bash
|
|
@@ -406,9 +442,9 @@ curl -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/events" \
|
|
|
406
442
|
"type": "COMMAND",
|
|
407
443
|
"title": "ReserveBike",
|
|
408
444
|
"fields": [
|
|
409
|
-
{"name": "customerId", "type": "String",
|
|
410
|
-
{"name": "bikeId", "type": "String",
|
|
411
|
-
{"name": "startTime", "type": "
|
|
445
|
+
{"name": "customerId", "type": "String", "example": "cust-42", "mapping": "session:customerId"},
|
|
446
|
+
{"name": "bikeId", "type": "String", "example": "bike-17", "mapping": "user-input"},
|
|
447
|
+
{"name": "startTime", "type": "DateTime", "example": "2026-06-01T09:00:00Z","mapping": "user-input"}
|
|
412
448
|
]
|
|
413
449
|
}
|
|
414
450
|
}]'
|
|
@@ -432,7 +468,18 @@ The timeline must always read left-to-right: SCREEN and COMMAND belong in the sa
|
|
|
432
468
|
|
|
433
469
|
After `place-element` returns the COMMAND node ID, create the arrows that complete the slice:
|
|
434
470
|
|
|
435
|
-
1. **SCREEN → COMMAND** — find the SCREEN node in the actor row of the same column
|
|
471
|
+
1. **SCREEN → COMMAND** — find the SCREEN node in the actor row of the same column.
|
|
472
|
+
|
|
473
|
+
**Prefer MCP** — there is no `cellId` filter on `get_nodes` (see note below), so read the chapter's cell map instead:
|
|
474
|
+
```
|
|
475
|
+
mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<CHAPTER_ID>" }
|
|
476
|
+
```
|
|
477
|
+
Read `meta.timelineData.cells["<actorRowId>-<columnId>"]` for the occupying node id (a cell id absent from that sparse array is empty — no SCREEN placed yet). Then connect with the type-checked edge tool, which auto-corrects direction and skips duplicates:
|
|
478
|
+
```
|
|
479
|
+
mcp__eventmodelers__set_connection { "boardId": "<BOARD_ID>", "source": "<screenNodeId>", "target": "<commandNodeId>", "action": "connect" }
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
**Fallback (no MCP):**
|
|
436
483
|
```bash
|
|
437
484
|
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=<actorRowId>-<columnId>" \
|
|
438
485
|
-H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" -H "x-user-id: eventmodeling-identifying-inputs"
|
|
@@ -445,7 +492,18 @@ After `place-element` returns the COMMAND node ID, create the arrows that comple
|
|
|
445
492
|
-d '{"source":"<screenNodeId>","target":"<commandNodeId>"}'
|
|
446
493
|
```
|
|
447
494
|
|
|
448
|
-
2. **COMMAND → EVENT** — find the EVENT node in the swimlane row of the same column
|
|
495
|
+
2. **COMMAND → EVENT** — find the EVENT node in the swimlane row of the same column.
|
|
496
|
+
|
|
497
|
+
**Prefer MCP** — same cell-map lookup, then connect:
|
|
498
|
+
```
|
|
499
|
+
mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<CHAPTER_ID>" }
|
|
500
|
+
```
|
|
501
|
+
Read `meta.timelineData.cells["<swimlaneRowId>-<columnId>"]` for the occupying node id, then:
|
|
502
|
+
```
|
|
503
|
+
mcp__eventmodelers__set_connection { "boardId": "<BOARD_ID>", "source": "<commandNodeId>", "target": "<eventNodeId>", "action": "connect" }
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
**Fallback (no MCP):**
|
|
449
507
|
```bash
|
|
450
508
|
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=<swimlaneRowId>-<columnId>" \
|
|
451
509
|
-H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" -H "x-user-id: eventmodeling-identifying-inputs"
|
|
@@ -458,6 +516,8 @@ After `place-element` returns the COMMAND node ID, create the arrows that comple
|
|
|
458
516
|
-d '{"source":"<commandNodeId>","target":"<eventNodeId>"}'
|
|
459
517
|
```
|
|
460
518
|
|
|
519
|
+
*Note:* `get_nodes` has no `cellId` filter (only `type`) — the `get_node`-on-CHAPTER + `meta.timelineData.cells` lookup above is the only way to check single-cell occupancy via MCP.
|
|
520
|
+
|
|
461
521
|
Skip a connection silently if the target cell is empty (the element may be placed in a later step). Log each created arrow: `→ connected SCREEN→COMMAND "PlaceOrder"` or `→ connected COMMAND→EVENT "PlaceOrder"→"OrderPlaced"`.
|
|
462
522
|
|
|
463
523
|
After all commands are placed and wired, present the Command Catalog summary as text to the user.
|
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md
CHANGED
|
@@ -11,6 +11,8 @@ allowed-tools:
|
|
|
11
11
|
|
|
12
12
|
> **Before doing anything else**, invoke the `connect` skill to resolve `TOKEN`, `BOARD_ID`, `ORG_ID`, and `BASE_URL`. Then invoke the `learn-eventmodelers-api` skill to load the full API reference. Do not proceed until both skills have been loaded.
|
|
13
13
|
|
|
14
|
+
Prefer `mcp__eventmodelers__*` tools when available (registered by the `connect` skill) — the curl blocks below are the fallback for sessions without MCP connected.
|
|
15
|
+
|
|
14
16
|
## Interview Phase (Optional)
|
|
15
17
|
|
|
16
18
|
**When to Interview**: Skip if the user has clearly identified: read model queries needed by UI, processor needs, and refresh patterns. Interview when unclear which data queries are critical or how frequently they're accessed.
|
|
@@ -408,12 +410,12 @@ Every field must also set `"cardinality"` — use `"Single"` unless the field ge
|
|
|
408
410
|
"type": "READMODEL",
|
|
409
411
|
"title": "ActiveReservationView",
|
|
410
412
|
"fields": [
|
|
411
|
-
{"name": "reservationId", "type": "String",
|
|
412
|
-
{"name": "customerId", "type": "String",
|
|
413
|
-
{"name": "bikeId", "type": "String",
|
|
414
|
-
{"name": "stationId", "type": "String",
|
|
415
|
-
{"name": "expiresAt", "type": "
|
|
416
|
-
{"name": "status", "type": "String",
|
|
413
|
+
{"name": "reservationId", "type": "String", "example": "res-001", "mapping": "BikeReserved.reservationId", "generated": false},
|
|
414
|
+
{"name": "customerId", "type": "String", "example": "cust-42", "mapping": "BikeReserved.customerId", "generated": false},
|
|
415
|
+
{"name": "bikeId", "type": "String", "example": "bike-17", "mapping": "BikeReserved.bikeId", "generated": false},
|
|
416
|
+
{"name": "stationId", "type": "String", "example": "stn-03", "mapping": "BikeReserved.stationId", "generated": false},
|
|
417
|
+
{"name": "expiresAt", "type": "DateTime", "example": "2026-06-01T09:30:00Z", "mapping": "ReservationConfirmed.expiresAt", "generated": false},
|
|
418
|
+
{"name": "status", "type": "String", "example": "confirmed", "mapping": "latest:ReservationConfirmed.status", "generated": false}
|
|
417
419
|
]
|
|
418
420
|
}
|
|
419
421
|
```
|
|
@@ -428,6 +430,35 @@ Read models go in the `interaction` lane — **in the same column as the SCREEN
|
|
|
428
430
|
|
|
429
431
|
**For a READMODEL** (interaction lane):
|
|
430
432
|
|
|
433
|
+
**Prefer MCP** — `place_element` collapses finding/creating the empty interaction cell (including inserting a new column when the target column is already occupied by a COMMAND) and creating the node into one call:
|
|
434
|
+
```
|
|
435
|
+
mcp__eventmodelers__place_element {
|
|
436
|
+
"boardId": "<BOARD_ID>",
|
|
437
|
+
"timelineId": "<CHAPTER_ID>",
|
|
438
|
+
"elementType": "READMODEL",
|
|
439
|
+
"title": "ActiveReservationView",
|
|
440
|
+
"columnIndex": <consumerScreenOrAutomationColumnIndex>
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
Then set `meta.fields` (with `mapping`/`generated`/`cardinality`) on the returned node id:
|
|
444
|
+
```
|
|
445
|
+
mcp__eventmodelers__submit_node_events {
|
|
446
|
+
"boardId": "<BOARD_ID>",
|
|
447
|
+
"events": [{
|
|
448
|
+
"id": "<event-uuid>", "eventType": "node:changed", "nodeId": "<returned-node-id>",
|
|
449
|
+
"boardId": "<BOARD_ID>", "timestamp": 1234567890,
|
|
450
|
+
"meta": {"type": "READMODEL", "title": "ActiveReservationView", "fields": [...]}
|
|
451
|
+
}]
|
|
452
|
+
}
|
|
453
|
+
```
|
|
454
|
+
To determine the consumer's column index beforehand, or to check whether a specific interaction cell is already occupied (there is no `cellId` filter on `get_nodes` — see the note under "Wire connections" below), fetch the chapter and read its cell map:
|
|
455
|
+
```
|
|
456
|
+
mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<CHAPTER_ID>" }
|
|
457
|
+
# → meta.timelineData.rows (find "interaction"/"actor" rows) and meta.timelineData.cells (sparse; absent id = empty)
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
**Fallback (no MCP)** — the full manual sequence:
|
|
461
|
+
|
|
431
462
|
1. Find the column where the consumer SCREEN or AUTOMATION lives. Fetch the timeline to get the interaction row ID:
|
|
432
463
|
```bash
|
|
433
464
|
curl -s -H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" \
|
|
@@ -460,6 +491,9 @@ Read models go in the `interaction` lane — **in the same column as the SCREEN
|
|
|
460
491
|
|
|
461
492
|
**For an AUTOMATION** (actor lane, same column as its READMODEL):
|
|
462
493
|
|
|
494
|
+
**Prefer MCP** — same `place_element` call with `"elementType": "AUTOMATION"`, then `submit_node_events` for fields, as above.
|
|
495
|
+
|
|
496
|
+
**Fallback (no MCP):**
|
|
463
497
|
1. Get the actor row ID from the same timeline fetch (row where `type === "actor"`).
|
|
464
498
|
2. `cellId = actorRow.id + "-" + columnId`
|
|
465
499
|
3. Create the AUTOMATION node using the same `node:created` pattern above with `"type": "AUTOMATION"` in `meta`.
|
|
@@ -492,7 +526,16 @@ For each view screen S that queries this read model:
|
|
|
492
526
|
|
|
493
527
|
After `place-element` returns the READMODEL node ID, create the arrows that complete the slice:
|
|
494
528
|
|
|
495
|
-
1. **EVENT → READMODEL** — find the primary source EVENT node in the swimlane row of the same column
|
|
529
|
+
1. **EVENT → READMODEL** — find the primary source EVENT node in the swimlane row of the same column.
|
|
530
|
+
|
|
531
|
+
**Prefer MCP** — `get_nodes` has no `cellId` filter (only `type`); look up occupancy via the chapter's cell map instead, then connect with the type-checked edge tool (auto-corrects direction, skips duplicates):
|
|
532
|
+
```
|
|
533
|
+
mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<CHAPTER_ID>" }
|
|
534
|
+
# → read meta.timelineData.cells["<swimlaneRowId>-<columnId>"] for the occupying node id
|
|
535
|
+
mcp__eventmodelers__set_connection { "boardId": "<BOARD_ID>", "source": "<eventNodeId>", "target": "<readmodelNodeId>", "action": "connect" }
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
**Fallback (no MCP):**
|
|
496
539
|
```bash
|
|
497
540
|
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=<swimlaneRowId>-<columnId>" \
|
|
498
541
|
-H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" -H "x-user-id: eventmodeling-identifying-outputs"
|
|
@@ -505,7 +548,14 @@ After `place-element` returns the READMODEL node ID, create the arrows that comp
|
|
|
505
548
|
-d '{"source":"<eventNodeId>","target":"<readmodelNodeId>"}'
|
|
506
549
|
```
|
|
507
550
|
|
|
508
|
-
2. **READMODEL → SCREEN** — connect to the existing SCREEN node in the actor row of the next column (screens are typically already placed from Step 3)
|
|
551
|
+
2. **READMODEL → SCREEN** — connect to the existing SCREEN node in the actor row of the next column (screens are typically already placed from Step 3).
|
|
552
|
+
|
|
553
|
+
**Prefer MCP:**
|
|
554
|
+
```
|
|
555
|
+
mcp__eventmodelers__set_connection { "boardId": "<BOARD_ID>", "source": "<readmodelNodeId>", "target": "<screenNodeId>", "action": "connect" }
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
**Fallback (no MCP):**
|
|
509
559
|
```bash
|
|
510
560
|
curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/connections" \
|
|
511
561
|
-H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" -H "x-user-id: eventmodeling-identifying-outputs" \
|
|
@@ -516,6 +566,13 @@ After `place-element` returns the READMODEL node ID, create the arrows that comp
|
|
|
516
566
|
3. **READMODEL → AUTOMATION** — if the read model is consumed by an automatic process (scheduler, background job, external trigger), place the AUTOMATION node and connect it:
|
|
517
567
|
- Place the AUTOMATION in the automation lane, in the column immediately to the right of its read model (same rule as screens).
|
|
518
568
|
- Then connect:
|
|
569
|
+
|
|
570
|
+
**Prefer MCP:**
|
|
571
|
+
```
|
|
572
|
+
mcp__eventmodelers__set_connection { "boardId": "<BOARD_ID>", "source": "<readmodelNodeId>", "target": "<automationNodeId>", "action": "connect" }
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
**Fallback (no MCP):**
|
|
519
576
|
```bash
|
|
520
577
|
curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/connections" \
|
|
521
578
|
-H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" -H "x-user-id: eventmodeling-identifying-outputs" \
|
|
@@ -11,6 +11,8 @@ allowed-tools:
|
|
|
11
11
|
|
|
12
12
|
> **Before doing anything else**, invoke the `connect` skill to resolve `TOKEN`, `BOARD_ID`, `ORG_ID`, and `BASE_URL`. Then invoke the `learn-eventmodelers-api` skill to load the full API reference. Do not proceed until both skills have been loaded.
|
|
13
13
|
|
|
14
|
+
Prefer `mcp__eventmodelers__*` tools when available (registered by the `connect` skill) — the curl blocks below are the fallback for sessions without MCP connected.
|
|
15
|
+
|
|
14
16
|
Coordinates the 10-step Event Modeling workflow. Each step delegates to a
|
|
15
17
|
specialized skill — this skill holds the sequence, transition conditions, and
|
|
16
18
|
what to carry forward between steps.
|
|
@@ -37,7 +39,15 @@ Placing all read models in new columns at the very end of the timeline severs th
|
|
|
37
39
|
|
|
38
40
|
After each step that creates elements (Steps 1–5), scan for any nodes that have no cell reference and are stranded at the default canvas position (0,0). These arise when `node:created` is called without `cellId`.
|
|
39
41
|
|
|
40
|
-
For each timeline in scope, check all node types that should be in cells
|
|
42
|
+
For each timeline in scope, check all node types that should be in cells.
|
|
43
|
+
|
|
44
|
+
**Prefer MCP:** call `get_nodes` once per type (each call returns the full node objects, including `meta.chapterId`/cell placement, so no separate cell-occupancy lookup is needed):
|
|
45
|
+
```
|
|
46
|
+
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "EVENT" }
|
|
47
|
+
```
|
|
48
|
+
Repeat with `"type": "COMMAND"`, `"READMODEL"`, `"SCREEN"`, `"AUTOMATION"`.
|
|
49
|
+
|
|
50
|
+
**Fallback (no MCP):**
|
|
41
51
|
```bash
|
|
42
52
|
for TYPE in EVENT COMMAND READMODEL SCREEN AUTOMATION; do
|
|
43
53
|
curl -s -H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" \
|
|
@@ -48,7 +58,14 @@ done
|
|
|
48
58
|
For each returned node, check whether it has a valid cell assignment. A node without a `cellId` (or with `chapterId` missing) is unplaced.
|
|
49
59
|
|
|
50
60
|
**For each unplaced node:**
|
|
51
|
-
- **If it belongs in the current model** → compute the correct `cellId` and
|
|
61
|
+
- **If it belongs in the current model** → compute the correct `cellId` and place it.
|
|
62
|
+
|
|
63
|
+
**Prefer MCP:** the node already exists but has never been assigned a cell, so this is a placement, not a repositioning — use `drop_node_to_cell` (not `move_node_in_timeline`, which is for moving a node that already occupies a different cell):
|
|
64
|
+
```
|
|
65
|
+
mcp__eventmodelers__drop_node_to_cell { "boardId": "$BOARD_ID", "timelineId": "<chapterId>", "cellId": "<rowId>-<colId>", "nodeId": "<nodeId>", "nodeType": "<TYPE>" }
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Fallback (no MCP):**
|
|
52
69
|
```bash
|
|
53
70
|
curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/events" \
|
|
54
71
|
-H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" -H "x-user-id: orchestrator" \
|
|
@@ -57,7 +74,14 @@ For each returned node, check whether it has a valid cell assignment. A node wit
|
|
|
57
74
|
"timestamp":1234567890,"chapterId":"<chapterId>","cellId":"<rowId>-<colId>",
|
|
58
75
|
"meta":{"type":"<TYPE>","title":"<title>"}}]'
|
|
59
76
|
```
|
|
60
|
-
- **If it is an orphan (duplicate or no longer needed)** → delete it
|
|
77
|
+
- **If it is an orphan (duplicate or no longer needed)** → delete it.
|
|
78
|
+
|
|
79
|
+
**Prefer MCP:**
|
|
80
|
+
```
|
|
81
|
+
mcp__eventmodelers__delete_node { "boardId": "$BOARD_ID", "nodeId": "<nodeId>" }
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Fallback (no MCP):**
|
|
61
85
|
```bash
|
|
62
86
|
curl -s -X DELETE "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/<nodeId>" \
|
|
63
87
|
-H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID"
|
|
@@ -194,7 +218,7 @@ Invoke `eventmodeling-storyboarding-events`.
|
|
|
194
218
|
human role, showing what data each screen displays and collects.
|
|
195
219
|
**Gate**: Every human role from the Role Catalog has at least one screen.
|
|
196
220
|
|
|
197
|
-
Use the
|
|
221
|
+
Use the `html-screen` skill to render a real HTML/CSS mockup for every screen — this is the default for each screen. Fall back to `storyboard-screen` (wireframe sketch) only when the user explicitly asked for sketches/wireframes.
|
|
198
222
|
|
|
199
223
|
You can reuse columns if screens can be matched to existing events, place the screen in the same
|
|
200
224
|
column as the event in the actor lane
|
|
@@ -10,6 +10,8 @@ allowed-tools:
|
|
|
10
10
|
|
|
11
11
|
> **Before doing anything else**, invoke the `connect` skill to resolve `TOKEN`, `BOARD_ID`, `ORG_ID`, and `BASE_URL`. Then invoke the `learn-eventmodelers-api` skill to load the full API reference. Do not proceed until both skills have been loaded.
|
|
12
12
|
|
|
13
|
+
Prefer `mcp__eventmodelers__*` tools when available (registered by the `connect` skill) — the curl blocks below are the fallback for sessions without MCP connected.
|
|
14
|
+
|
|
13
15
|
Arrange all brainstormed events chronologically to create a logical sequence that makes sense as a narrative timeline. Show how events flow and depend on each other.
|
|
14
16
|
|
|
15
17
|
## Workflow
|
|
@@ -101,6 +103,12 @@ Timelines (chapters) are **created and assigned during Step 1 (Brainstorming)**.
|
|
|
101
103
|
|
|
102
104
|
Before placing events, resolve the target timeline:
|
|
103
105
|
|
|
106
|
+
Prefer MCP:
|
|
107
|
+
```
|
|
108
|
+
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "CHAPTER" }
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Fallback (no MCP):**
|
|
104
112
|
```bash
|
|
105
113
|
curl -s -H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" \
|
|
106
114
|
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=CHAPTER"
|
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: eventmodeling-slicing-event-models
|
|
3
|
-
description: "Identify feature slices directly from a completed event model's timeline and create slice definitions on the board — each COMMAND becomes a state-change slice, each READMODEL becomes a state-view slice, each AUTOMATION becomes an automation slice. Use after completing event modeling to define slice boundaries and note event dependencies between them. Do not use for: organizational team structure based on Conway's Law (use eventmodeling-applying-conways-law)
|
|
3
|
+
description: "Identify feature slices directly from a completed event model's timeline and create slice definitions on the board — each COMMAND becomes a state-change slice, each READMODEL becomes a state-view slice, each AUTOMATION becomes an automation slice. Use after completing event modeling to define slice boundaries and note event dependencies between them. Do not use for: organizational team structure based on Conway's Law (use eventmodeling-applying-conways-law), planning before the event model is complete (complete the full model first using eventmodeling-orchestrating-event-modeling), or 'add the next slice' when every existing element already has one (use add-next-slice — this skill only makes existing elements explicit, it never invents new ones)."
|
|
4
4
|
allowed-tools:
|
|
5
5
|
- AskUserQuestion
|
|
6
6
|
- Write
|
|
@@ -11,6 +11,8 @@ allowed-tools:
|
|
|
11
11
|
|
|
12
12
|
> **Before doing anything else**, invoke the `connect` skill to resolve `TOKEN`, `BOARD_ID`, `ORG_ID`, and `BASE_URL`. Then invoke the `learn-eventmodelers-api` skill to load the full API reference (in particular the **Slices** section). Do not proceed until both skills have been loaded.
|
|
13
13
|
|
|
14
|
+
> Prefer `mcp__eventmodelers__*` tools when available (registered by the `connect` skill) — the curl blocks below are the fallback for sessions without MCP connected.
|
|
15
|
+
|
|
14
16
|
**Purpose**: Turn a completed event model's timeline into explicit slice definitions on the board, and note the event dependencies between them.
|
|
15
17
|
|
|
16
18
|
**When to Use**:
|
|
@@ -55,6 +57,12 @@ Walk the timeline column by column:
|
|
|
55
57
|
|
|
56
58
|
`$TL` (the timeline/chapter UUID) is required for every call below. If it wasn't given up front, resolve it before doing anything else:
|
|
57
59
|
|
|
60
|
+
Prefer MCP:
|
|
61
|
+
```
|
|
62
|
+
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "CHAPTER" }
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Fallback (no MCP):**
|
|
58
66
|
```bash
|
|
59
67
|
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=CHAPTER"
|
|
60
68
|
```
|
|
@@ -67,6 +75,12 @@ curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=CHAPTER"
|
|
|
67
75
|
|
|
68
76
|
Use `spec-info` (or existing board knowledge) to list every COMMAND, READMODEL, and AUTOMATION node across the resolved timeline:
|
|
69
77
|
|
|
78
|
+
Prefer MCP:
|
|
79
|
+
```
|
|
80
|
+
mcp__eventmodelers__get_spec_info { "boardId": "<BOARD_ID>", "timelineId": "<TL>" }
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Fallback (no MCP):**
|
|
70
84
|
```bash
|
|
71
85
|
curl "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/timelines/$TL/spec-info" -H "x-token: $TOKEN"
|
|
72
86
|
# → { timelineId, elements: [{ id, title, type }] }
|
|
@@ -76,6 +90,12 @@ Filter to `type` in `COMMAND`, `READMODEL`, `AUTOMATION`.
|
|
|
76
90
|
|
|
77
91
|
`spec-info` doesn't include the column each element sits in, so fetch the chapter node to resolve it:
|
|
78
92
|
|
|
93
|
+
Prefer MCP:
|
|
94
|
+
```
|
|
95
|
+
mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<TL>" }
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Fallback (no MCP):**
|
|
79
99
|
```bash
|
|
80
100
|
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$TL" -H "x-token: $TOKEN"
|
|
81
101
|
# → meta.timelineData.columns: [{ id, index }]
|
|
@@ -86,6 +106,12 @@ For each filtered element, find the cell whose `nodeId` matches the element's `i
|
|
|
86
106
|
|
|
87
107
|
Check which columns already have a slice, so you don't create duplicates:
|
|
88
108
|
|
|
109
|
+
Prefer MCP:
|
|
110
|
+
```
|
|
111
|
+
mcp__eventmodelers__list_slices { "boardId": "<BOARD_ID>" }
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Fallback (no MCP):**
|
|
89
115
|
```bash
|
|
90
116
|
curl $BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata/slices -H "x-token: $TOKEN"
|
|
91
117
|
# → { slices: [{ id, title, status }] }
|
|
@@ -97,6 +123,12 @@ A column already has a slice if its element's title matches an existing slice's
|
|
|
97
123
|
|
|
98
124
|
For each column from Step 2 that doesn't already have a matching slice, mark that **existing** column as a slice via the **slice-definitions** endpoint:
|
|
99
125
|
|
|
126
|
+
Prefer MCP:
|
|
127
|
+
```
|
|
128
|
+
mcp__eventmodelers__create_slice_definition { "boardId": "<BOARD_ID>", "timelineId": "<TL>", "columnId": "<colId>", "title": "PlaceOrder" }
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Fallback (no MCP):**
|
|
100
132
|
```bash
|
|
101
133
|
curl -X POST $BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/timelines/$TL/slice-definitions \
|
|
102
134
|
-H "x-token: $TOKEN" -H "Content-Type: application/json" \
|
|
@@ -108,7 +140,9 @@ curl -X POST $BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/timelines/$TL/slice-defi
|
|
|
108
140
|
- READMODEL column → title = read model name (state-view slice)
|
|
109
141
|
- AUTOMATION column → title = automation name, or the command it issues (automation slice)
|
|
110
142
|
|
|
111
|
-
Use **`slice-definitions`**, never the plain **`slices`** endpoint here — `slices` creates a brand-new column with its own swimlane/content nodes, which would duplicate the element already placed on the timeline. `slice-definitions` only adds a `SLICE_BORDER` node to the column you already resolved in Step 2. `title` always comes from the request body — it is never derived automatically from the command/read model/automation node.
|
|
143
|
+
Use **`create_slice_definition`/`slice-definitions`**, never `create_slice`/the plain **`slices`** endpoint here — `create_slice`/`slices` creates a brand-new column with its own swimlane/content nodes, which would duplicate the element already placed on the timeline. `create_slice_definition`/`slice-definitions` only adds a `SLICE_BORDER` node to the column you already resolved in Step 2. `title` always comes from the request body — it is never derived automatically from the command/read model/automation node.
|
|
144
|
+
|
|
145
|
+
**If Step 2 finds nothing to slice** (every COMMAND/READMODEL/AUTOMATION on the timeline already has a matching `SLICE_BORDER`), this skill's job is done — there is no existing element left to make explicit. Do not invent new model content here; that is out of scope for a skill whose whole design assumes the model is already complete. Invoke the `add-next-slice` skill instead — it owns deciding on and creating a genuinely new slice from scratch.
|
|
112
146
|
|
|
113
147
|
## Step 4 (Optional): Note Dependencies Between Slices
|
|
114
148
|
|