@eventmodelers/cli 1.0.36 → 1.0.37

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.
Files changed (69) hide show
  1. package/package.json +1 -1
  2. package/shared/skills/learn-eventmodelers-api/SKILL.md +12 -10
  3. package/stacks/modeling-kit/templates/.claude/skills/add-next-slice/SKILL.md +2 -23
  4. package/stacks/modeling-kit/templates/.claude/skills/add-next-slice/references/api-fallback.md +11 -0
  5. package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/SKILL.md +6 -57
  6. package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/references/api-fallback.md +68 -0
  7. package/stacks/modeling-kit/templates/.claude/skills/attributes/SKILL.md +4 -61
  8. package/stacks/modeling-kit/templates/.claude/skills/attributes/references/api-fallback.md +39 -0
  9. package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/SKILL.md +9 -53
  10. package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/references/api-fallback.md +63 -0
  11. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-applying-conways-law/SKILL.md +9 -319
  12. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-applying-conways-law/references/examples.md +329 -0
  13. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/SKILL.md +23 -199
  14. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/references/api-fallback.md +97 -0
  15. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/references/examples.md +35 -0
  16. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/SKILL.md +13 -410
  17. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/references/api-fallback.md +22 -0
  18. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/references/examples.md +397 -0
  19. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-automation-chains/SKILL.md +132 -0
  20. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-automation-chains/references/api-fallback.md +21 -0
  21. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/SKILL.md +9 -236
  22. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/references/examples.md +257 -0
  23. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +28 -302
  24. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/references/api-fallback.md +31 -0
  25. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/references/examples.md +216 -0
  26. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/SKILL.md +30 -343
  27. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/references/api-fallback.md +79 -0
  28. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/references/examples.md +282 -0
  29. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md +51 -400
  30. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/references/api-fallback.md +67 -0
  31. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/references/examples.md +273 -0
  32. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/SKILL.md +45 -152
  33. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/domain-patterns.md +49 -90
  34. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/patterns.md +64 -137
  35. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/SKILL.md +74 -65
  36. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/references/api-fallback.md +51 -0
  37. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/SKILL.md +1 -5
  38. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/references/api-fallback.md +10 -0
  39. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/SKILL.md +19 -36
  40. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/api-fallback.md +41 -0
  41. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/examples.md +12 -9
  42. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/patterns.md +1 -10
  43. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/SKILL.md +26 -332
  44. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/references/api-fallback.md +77 -0
  45. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/references/examples.md +271 -0
  46. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-translating-external-events/SKILL.md +9 -294
  47. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-translating-external-events/references/examples.md +306 -0
  48. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/SKILL.md +12 -11
  49. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/references/api-fallback.md +14 -0
  50. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/SKILL.md +6 -36
  51. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/references/api-fallback.md +14 -0
  52. package/stacks/modeling-kit/templates/.claude/skills/examples/SKILL.md +3 -110
  53. package/stacks/modeling-kit/templates/.claude/skills/examples/references/api-fallback.md +118 -0
  54. package/stacks/modeling-kit/templates/.claude/skills/handle-comment/SKILL.md +5 -25
  55. package/stacks/modeling-kit/templates/.claude/skills/handle-comment/references/api-fallback.md +35 -0
  56. package/stacks/modeling-kit/templates/.claude/skills/html-screen/SKILL.md +9 -44
  57. package/stacks/modeling-kit/templates/.claude/skills/html-screen/references/api-fallback.md +51 -0
  58. package/stacks/modeling-kit/templates/.claude/skills/place-element/SKILL.md +23 -183
  59. package/stacks/modeling-kit/templates/.claude/skills/place-element/references/api-fallback.md +193 -0
  60. package/stacks/modeling-kit/templates/.claude/skills/storyboard/SKILL.md +14 -81
  61. package/stacks/modeling-kit/templates/.claude/skills/storyboard/references/api-fallback.md +74 -0
  62. package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/SKILL.md +4 -45
  63. package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/references/api-fallback.md +44 -0
  64. package/stacks/modeling-kit/templates/.claude/skills/timeline/SKILL.md +19 -88
  65. package/stacks/modeling-kit/templates/.claude/skills/timeline/references/api-fallback.md +91 -0
  66. package/stacks/modeling-kit/templates/.claude/skills/update-prompt-status/SKILL.md +1 -9
  67. package/stacks/modeling-kit/templates/.claude/skills/update-prompt-status/references/api-fallback.md +14 -0
  68. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-integrating-legacy-systems/SKILL.md +0 -674
  69. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/snapshotting.md +0 -204
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventmodelers/cli",
3
- "version": "1.0.36",
3
+ "version": "1.0.37",
4
4
  "description": "Eventmodelers CLI — real-time Claude agent + skills for Claude Code, for any stack (Node, Supabase, Axon, Cratis, or modeling-only)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -20,18 +20,18 @@ Server name: `eventmodelers`. Every tool takes `boardId` explicitly; none need `
20
20
  | Tool | Args | Purpose | REST equivalent |
21
21
  |---|---|---|---|
22
22
  | `list_boards` | — | List boards for the org | §1 `GET /api/boards` (org-scoped) |
23
- | `get_nodes` | `boardId`, `type?`, `name?` | List nodes, optionally by type and/or a partial case-insensitive title match | §3 `GET .../nodes` |
24
- | `get_node` | `boardId`, `nodeId` | Get one node | §3 `GET .../nodes/:nodeId` |
23
+ | `get_nodes` | `boardId`, `type?`, `name?`, `chapterId?`, `nodeIds?`, `projection?` (`"line"`) | List nodes, optionally by type and/or a partial case-insensitive title match. `chapterId` scopes to one timeline — prefer this over an unscoped board-wide call whenever the step is working within one chapter (the common case); `nodeIds` fetches a known, scattered subset in one call (e.g. re-verifying exactly the nodes just touched by a batch write) instead of a full `type` refetch; `projection: "line"` maps each match to `{id, type, title}` only | §3 `GET .../nodes` |
24
+ | `get_node` | `boardId`, `nodeId`, `projection?` (`"cells"` \| `"edges"`) | Get one node. `projection: "cells"` (CHAPTER nodes only) returns just `{rows, columns, cells}` instead of the full `timelineData` — use whenever only the grid/occupancy is needed, not the whole chapter; `projection: "edges"` returns just that node's inbound/outbound connections instead of `findNodeById`'s full record. Both are opt-in — omitting `projection` is the unchanged full response | §3 `GET .../nodes/:nodeId` |
25
25
  | `get_node_comments` | `boardId`, `nodeId` | List comments on a node | §1 `GET .../nodes/:nodeId/comments` |
26
26
  | `get_board_events` | `boardId` | All board events, in sequence | §1 `GET .../events` |
27
27
  | `search_board_events` | `boardId`, `name` | Search events by node name | §1 `GET .../events/search` |
28
- | `submit_node_events` | `boardId`, `events[]` | Create/update nodes (raw `NodeChangeEvent`/edge events) | §3 `POST .../nodes/events` |
28
+ | `submit_node_events` | `boardId`, `events[]`, `autoConnect?`, `compact?` | Create/update nodes (raw `NodeChangeEvent`/edge events). `autoConnect: false` places freshly-created nodes without wiring them to their own/previous-column neighbors (avoids a stray nearest-left edge); `compact: true` returns `{persisted: <count>}` instead of the per-node hash map | §3 `POST .../nodes/events` |
29
29
  | `delete_node` | `boardId`, `nodeId` | Delete a node. Deleting a chapter (timeline) cascades — every node placed in one of its cells, plus any node parented to it (e.g. SLICE_BORDER), is deleted too, along with all their edges | (via `node:deleted` event, §3) |
30
30
  | `create_drawing` | `boardId`, `kind`, `x`, `y`, `width`, `height`, ... | Freehand canvas annotation (path/rect/text) — never placed in a cell | — (no REST equivalent; MCP-only) |
31
31
  | `find_nodes_in_drawing` | `boardId`, `drawingId` | Nodes fully contained inside a drawing's bounding box | — (no REST equivalent; MCP-only) |
32
32
  | `create_chapter` | `boardId`, `x?`, `y?` | Create a timeline. Omitting `x`/`y` auto-stacks it below the lowest existing chapter (by its *actual current* row-height total, not the height it was created with — safe even after `add_lane` growth), plus a fixed margin | §2 `POST .../chapters` |
33
33
  | `get_chapter_bounds` | `boardId` | Absolute canvas bounding box `{id, title, x, y, width, height}` of every chapter on the board — width/height derived from each chapter's current row/column layout, not a guessed default. Use before picking explicit `x`/`y` for `create_chapter` (e.g. placing below the chapter with the largest `y + height`) to avoid overlapping one that grew since it was created | §2 `GET .../chapters/bounds` |
34
- | `add_column` | `boardId`, `timelineId`, `index?` | Add a column | §2 `POST .../timelines/:id/columns` |
34
+ | `add_column` | `boardId`, `timelineId`, `index?`, `beforeNodeId?`, `afterNodeId?`, `count?` | Add one or more columns in one call. `count` inserts that many contiguously starting at the insertion point (default 1). Position with at most one of `index` (0-based), `beforeNodeId`, or `afterNodeId` (resolves the index from where that already-placed node currently sits) — omit all three to append | §2 `POST .../timelines/:id/columns` |
35
35
  | `delete_column` | `boardId`, `timelineId`, `columnId` | Delete a column | §2 `DELETE .../columns/:columnId` |
36
36
  | `add_lane` | `boardId`, `timelineId`, `type`, `label?`, `index?` | Add a lane/row | §2 `POST .../timelines/:id/lanes` |
37
37
  | `remove_lane` | `boardId`, `timelineId`, `rowId` | Remove a lane | — (extends §2; no direct REST route) |
@@ -42,19 +42,21 @@ Server name: `eventmodelers`. Every tool takes `boardId` explicitly; none need `
42
42
  | `clear_cell` | `boardId`, `timelineId`, `cellId` | Unassign the node from a cell without deleting it — the cell becomes empty and the node survives (unplaced); no-op if already empty. Use `delete_node` to remove the node entirely | — (MCP-only convenience) |
43
43
  | `create_slice` | `boardId`, `timelineId`, `type`, `index?`, `nodes?: {actor?, interaction?, swimlane?}` (each `{rowId?, title?}`) | Create a full slice (column + nodes + SLICE_BORDER). `rowId` targets a specific lane when the chapter has more than one lane of that type (e.g. several actor lanes); omit to use the first matching lane | §5 `POST .../slices` |
44
44
  | `create_slice_definition` | `boardId`, `timelineId`, `columnId`, `title`, `data?`, `meta?` | Create a SLICE_BORDER over an existing column | §5 `POST .../slice-definitions` |
45
- | `place_element` | `boardId`, `timelineId`, `elementType`, `title`, `columnIndex?` | Find/create an empty cell in the right lane and place a COMMAND/READMODEL/EVENT | — (MCP-only convenience; composes §2+§3) |
45
+ | `place_element` | `boardId`, `timelineId`, `elementType`, `title`, `columnIndex?`, `compact?`, `autoConnect?` | Find/create an empty cell in the right lane and place a COMMAND/READMODEL/EVENT. `autoConnect: false` places without wiring to timeline neighbors — wire the edges yourself | — (MCP-only convenience; composes §2+§3) |
46
46
  | `list_slices` | `boardId` | List slices (id, title, status) | §8 `GET .../slicedata/slices` |
47
47
  | `update_slice_status` | `boardId`, `sliceId`, `newStatus` | Change a SLICE_BORDER's `sliceStatus` | — (via `node:changed` event, §3) |
48
48
  | `get_slice_data` | `boardId`, `contextName?`, `contextId?`, `sliceId?` | Full element graph for slices in a context | §8 `GET /slicedata` |
49
- | `get_spec_info` | `boardId`, `timelineId` | EVENT/COMMAND/READMODEL nodes valid in GWT steps | §6 `GET .../spec-info` |
50
- | `add_scenario` | `boardId`, `timelineId`, `columnId`, `scenarios[]` | Append GWT scenario(s) to a column's spec node | §6 `POST .../scenarios` |
51
- | `add_storyline` | `boardId`, `timelineId`, `columnId`, `storylines[]` | **Experimental — only use when explicitly asked for a storyline/walkthrough.** Append storyline(s) (ordered, branchable beats over existing elements) to a column's spec node | §6 `POST .../storylines` |
52
- | `set_connection` | `boardId`, `source`, `target`, `action` (`'connect'\|'remove'`) | Add or remove a type-checked directed edge | (via `edges` on §3 events) |
49
+ | `get_spec_info` | `boardId`, `timelineId`, `elementTypes?` | EVENT/COMMAND/READMODEL nodes valid in GWT steps. Pass `elementTypes` (subset of `EVENT`/`COMMAND`/`READMODEL`) to avoid pulling the full element list when only one or two types are needed — filtered server-side, not just after a full fetch | §6 `GET .../spec-info` |
50
+ | `get_board_outline` | `boardId`, `chapterId` | One chapter's structure, compact: per-column node lists (`{id, type, title, lane}`) + a flat edge list, no HTML pages / field bodies / meta. The cheap "what is where and how is it wired" read — prefer over `get_nodes` (no projection) for orientation checks | — (MCP-only convenience) |
51
+ | `validate_model` | `boardId`, `chapterId`, `checks?[]` | Server-side Event Modeling structural checklist over one chapter compact `findings` only. Checks: unplaced nodes, backward arrows (with the todo-list `EVENT→READMODEL` exception), zero/multi-issuer commands, sourceless read models, two-screens-in-a-column, missing scenarios. Replaces the manual per-type `get_nodes` + `get_node projection=edges` validation pass | — (MCP-only convenience) |
52
+ | `add_scenario` | `boardId`, `timelineId`, `columnId`, `scenarios[]`, `compact?` | Append GWT scenario(s) to a column's spec node. `compact: true` returns `{specNodeId, added, scenarioCount, isNewNode}` instead of echoing every scenario back | §6 `POST .../scenarios` |
53
+ | `add_storyline` | `boardId`, `timelineId`, `columnId`, `storylines[]`, `compact?` | **Experimental — only use when explicitly asked for a storyline/walkthrough.** Append storyline(s) (ordered, branchable beats over existing elements) to a column's spec node. `compact: true` suppresses the full storyline echo | §6 `POST .../storylines` |
54
+ | `set_connection` | `boardId`, `source`, `target`, `action` (`'connect'\|'remove'`) | Add or remove a type-checked directed edge. Batch form `set_connections` takes `connections[]` (applied in order) plus `compact?` — `compact: true` returns a `{connected, existed, removed, notFound, failed, errors}` tally instead of one row per edge | — (via `edges` on §3 events) |
53
55
  | `auto_connect_node` | `boardId`, `nodeId` | Re-run auto-connect for a node | §3 `POST .../nodes/:nodeId/auto-connect` |
54
56
  | `link_element` | `boardId`, `nodeId`, `targetNodeId` | Link two existing same-type nodes: `targetNodeId` is replaced with a full copy of `nodeId`'s meta plus `meta.linkedTo`. Linking means first create, then link | §3 `POST .../nodes/:nodeId/link` |
55
57
  | `add_comment` | `boardId`, `nodeId`, `text`, `type?` (`'COMMENT'\|'TASK'\|'QUESTION'`), `author?` | Add a comment — `QUESTION` flags gaps/edge cases during review | — (via comment events) |
56
58
  | `update_comment` | `boardId`, `nodeId`, `commentId`, `action` (`'resolve'\|'delete'`) | Resolve or delete a comment | — (via comment events) |
57
- | `create_screen` | `boardId`, `contentType` (`'image'\|'sketch'\|'html'`), `nodeId?`, `chapterId`, `cellId?`/`cellName?`, plus content fields (`imageBase64`/`mimeType`, `elements[]`, or `pages[]`/`backgroundColor`), `description?` | Create + place a new screen node (SCREEN or HTML_SCREEN) atomically, in one call | §4 `POST .../images/:id/sketch` + `image-nodes` |
59
+ | `create_screen` | `boardId`, `contentType` (`'image'\|'sketch'\|'html'`), `nodeId?`, `chapterId`, `cellId?`/`cellName?`, plus content fields (`imageBase64`/`mimeType`, `elements[]`, or `pages[]`/`backgroundColor`), `description?`, `fields?`, `autoConnect?` | Create + place a new screen node (SCREEN or HTML_SCREEN) atomically, in one call. Batch form `create_screens` takes `screens[]` (HTML only) + `autoConnect?`. `autoConnect: false` places without wiring to timeline neighbors | §4 `POST .../images/:id/sketch` + `image-nodes` |
58
60
  | `render_screen` | `boardId`, `nodeId`, `elements[]?` (SCREEN) or `pages[]?`+`backgroundColor?` (HTML_SCREEN), `description?` | Update an existing screen's content — exactly one of `elements`/`pages` | §4 `POST .../images/:id/sketch` + `image-nodes` |
59
61
  | `add_field_examples` | `boardId`, `nodeId?`, `name?`, `cellName?`, `timelineId?` | Fill empty field examples using linked-node context | — (MCP-only convenience) |
60
62
  | `get_attribute_chain` | `boardId`, `timelineId`, `targetCellName`, `sourceCellName` | Resolve every node between two cells, ordered target→source | — (MCP-only convenience) |
@@ -15,23 +15,7 @@ This is the counterpart to `eventmodeling-slicing-event-models`, which only ever
15
15
 
16
16
  ## Core Concept: A Slice Is One Command, One Read Model, or One Automation — Never Combined
17
17
 
18
- Same rules as `eventmodeling-slicing-event-models` — the slice you create here must obey them too, not just slices made from pre-existing elements.
19
-
20
- A **Feature Slice** is the thinnest possible vertical cut through the model — exactly one decision or one query:
21
-
22
- ```
23
- state-change slice = SCREEN/Processor → COMMAND → EVENT(s)
24
- state-view slice = EVENT(s) → READMODEL → SCREEN/Processor
25
- automation slice = EVENT(s) → AUTOMATION → COMMAND → EVENT(s)
26
- ```
27
-
28
- A slice never mixes a COMMAND and a READMODEL — the platform models these as two distinct slice types (`state-change` and `state-view`). If the capability you decide on in Step 1 needs both a command and a read model (e.g. "place an order" needs the `PlaceOrder` command *and* an `OrderDetailView` read model), that's **two slices** — create them one at a time, each its own `create_slice` call.
29
-
30
- **Key characteristics**:
31
- - Exactly one COMMAND (state-change), exactly one READMODEL (state-view), or one AUTOMATION's command — never combined
32
- - Named after that command, read model, or automation
33
- - Independently deployable
34
- - Communicates with other slices via events only
18
+ Same rules as `eventmodeling-slicing-event-models`'s own "Core Concept" section see there for the full definition (the state-change/state-view/automation shapes, key characteristics) — the slice you create here must obey them too, not just slices made from pre-existing elements. If the capability you decide on in Step 1 needs both a command and a read model (e.g. "place an order" needs the `PlaceOrder` command *and* an `OrderDetailView` read model), that's **two slices** — create them one at a time, each its own `create_slice` call.
35
19
 
36
20
  ---
37
21
 
@@ -65,12 +49,7 @@ Pick `type` based on what you decided in Step 1 — `state-change` for a new com
65
49
 
66
50
  If the chapter has more than one lane of the same type (e.g. several actor lanes for different user roles), `create_slice` places each node in the **first** matching lane by default — pass `nodes.<actor|interaction|swimlane>.rowId` (the target row's id, from the chapter's `timelineData.rows`) to target a specific lane instead of the first one.
67
51
 
68
- **Fallback (no MCP):**
69
- ```bash
70
- curl -X POST $BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/timelines/$TL/slices \
71
- -H "x-token: $TOKEN" -H "Content-Type: application/json" \
72
- -d '{"type":"state-change","nodes":{"interaction":{"title":"CancelReservation"}}}'
73
- ```
52
+ **Fallback (no MCP):** see `references/api-fallback.md` — "Step 2: Create the Slice".
74
53
 
75
54
  ## Step 3: Replace the placeholder screen — matching the board's existing style
76
55
 
@@ -0,0 +1,11 @@
1
+ # Add Next Slice — curl Fallback Calls
2
+
3
+ Only needed when MCP is not connected. Every call below has an MCP equivalent in the main SKILL.md — always prefer that.
4
+
5
+ ## Step 2: Create the Slice
6
+
7
+ ```bash
8
+ curl -X POST $BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/timelines/$TL/slices \
9
+ -H "x-token: $TOKEN" -H "Content-Type: application/json" \
10
+ -d '{"type":"state-change","nodes":{"interaction":{"title":"CancelReservation"}}}'
11
+ ```
@@ -33,16 +33,7 @@ If `boardId` is explicitly passed it overrides `BOARD_ID` from `connect`.
33
33
  mcp__eventmodelers__list_slices { "boardId": "$BOARD_ID" }
34
34
  ```
35
35
 
36
- **Fallback (no MCP):**
37
- ```bash
38
- curl -s \
39
- -H "x-token: $TOKEN" \
40
- -H "x-board-id: $BOARD_ID" \
41
- -H "x-user-id: analyze-existing-model" \
42
- "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata/slices"
43
- ```
44
-
45
- Response: `{ "slices": [{ "id": "<uuid>", "title": "<name>", "status": "<status>" }] }`
36
+ **Fallback (no MCP):** see `references/api-fallback.md` — "Step 2 — List all slices".
46
37
 
47
38
  Save the full slice list. Count total slices and group by status:
48
39
 
@@ -61,14 +52,7 @@ Fetch all `MODEL_CONTEXT` nodes to identify bounded contexts on the board:
61
52
  mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "MODEL_CONTEXT" }
62
53
  ```
63
54
 
64
- **Fallback (no MCP):**
65
- ```bash
66
- curl -s \
67
- -H "x-token: $TOKEN" \
68
- -H "x-board-id: $BOARD_ID" \
69
- -H "x-user-id: analyze-existing-model" \
70
- "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=MODEL_CONTEXT"
71
- ```
55
+ **Fallback (no MCP):** see `references/api-fallback.md` — "Step 3 — Discover contexts".
72
56
 
73
57
  - If a `contextName` argument was given, filter to that single context and skip others.
74
58
  - If no `MODEL_CONTEXT` nodes exist, continue with a single unnamed context scope.
@@ -85,20 +69,7 @@ For each resolved context, fetch the full element graph:
85
69
  mcp__eventmodelers__get_slice_data { "boardId": "$BOARD_ID", "contextName": "<CONTEXT_NAME>" }
86
70
  ```
87
71
 
88
- **Fallback (no MCP):**
89
- ```bash
90
- curl -s \
91
- -H "x-token: $TOKEN" \
92
- -H "x-board-id: $BOARD_ID" \
93
- -H "x-user-id: analyze-existing-model" \
94
- "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata?contextName=<CONTEXT_NAME>"
95
- ```
96
-
97
- Each response contains a `slices` array. Each slice entry includes:
98
- - `id`, `title`, `status`
99
- - `elements`: array of `{ type, id, title, fields[] }` — element types: `EVENT`, `COMMAND`, `READMODEL`, `SCREEN`, `AUTOMATION`
100
- - `specs`: array of GWT scenarios (may be empty)
101
- - `edges`: relationships between elements
72
+ **Fallback (no MCP):** see `references/api-fallback.md` — "Step 4 — Fetch slice data per context".
102
73
 
103
74
  Fetch all contexts in parallel if there are multiple. Merge results, keyed by context name.
104
75
 
@@ -132,6 +103,8 @@ For each slice, check whether `specs` is non-empty. Calculate:
132
103
 
133
104
  ### 5d — Structural gaps per slice
134
105
 
106
+ For a fast structural read per chapter, call `validate_model` (`{boardId, chapterId}`) — it returns unplaced nodes, backward arrows, zero/multi-issuer commands, sourceless read models, screen-per-column collisions and missing scenarios in one compact response, which covers most of the gaps this section looks for without walking every slice by hand. For a plain "what is where and how is it wired" dump (no field bodies or screen HTML), `get_board_outline` (`{boardId, chapterId}`) is the cheap fetch. Use the per-slice table below for anything those two don't answer.
107
+
135
108
  For every slice, determine its type from the elements present and check for common structural issues:
136
109
 
137
110
  | Slice type | Expect | Flag if missing |
@@ -216,28 +189,4 @@ mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "MODEL_CONTEXT"
216
189
  mcp__eventmodelers__get_slice_data { "boardId": "$BOARD_ID", "contextName": "Ordering" }
217
190
  ```
218
191
 
219
- **Fallback (no MCP):**
220
- ```bash
221
- # 1. List slices
222
- curl -s \
223
- -H "x-token: $TOKEN" \
224
- -H "x-board-id: $BOARD_ID" \
225
- -H "x-user-id: analyze-existing-model" \
226
- "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata/slices"
227
-
228
- # 2. Fetch MODEL_CONTEXT nodes
229
- curl -s \
230
- -H "x-token: $TOKEN" \
231
- -H "x-board-id: $BOARD_ID" \
232
- -H "x-user-id: analyze-existing-model" \
233
- "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=MODEL_CONTEXT"
234
-
235
- # 3. Fetch full slice data for a context
236
- curl -s \
237
- -H "x-token: $TOKEN" \
238
- -H "x-board-id: $BOARD_ID" \
239
- -H "x-user-id: analyze-existing-model" \
240
- "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata?contextName=Ordering"
241
- ```
242
-
243
- Replace `$TOKEN`, `$ORG_ID`, `$BOARD_ID`, and the context name with real values resolved from the `connect` skill.
192
+ **Fallback (no MCP):** see `references/api-fallback.md` — "Example — full board analysis".
@@ -0,0 +1,68 @@
1
+ # Analyze Existing Model — curl Fallback Calls
2
+
3
+ Only needed when MCP is not connected. Every call below has an MCP equivalent in the main SKILL.md — always prefer that.
4
+
5
+ ## Step 2 — List all slices
6
+
7
+ ```bash
8
+ curl -s \
9
+ -H "x-token: $TOKEN" \
10
+ -H "x-board-id: $BOARD_ID" \
11
+ -H "x-user-id: analyze-existing-model" \
12
+ "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata/slices"
13
+ ```
14
+
15
+ Response: `{ "slices": [{ "id": "<uuid>", "title": "<name>", "status": "<status>" }] }`
16
+
17
+ ## Step 3 — Discover contexts
18
+
19
+ ```bash
20
+ curl -s \
21
+ -H "x-token: $TOKEN" \
22
+ -H "x-board-id: $BOARD_ID" \
23
+ -H "x-user-id: analyze-existing-model" \
24
+ "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=MODEL_CONTEXT"
25
+ ```
26
+
27
+ ## Step 4 — Fetch slice data per context
28
+
29
+ ```bash
30
+ curl -s \
31
+ -H "x-token: $TOKEN" \
32
+ -H "x-board-id: $BOARD_ID" \
33
+ -H "x-user-id: analyze-existing-model" \
34
+ "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata?contextName=<CONTEXT_NAME>"
35
+ ```
36
+
37
+ Each response contains a `slices` array. Each slice entry includes:
38
+ - `id`, `title`, `status`
39
+ - `elements`: array of `{ type, id, title, fields[] }` — element types: `EVENT`, `COMMAND`, `READMODEL`, `SCREEN`, `AUTOMATION`
40
+ - `specs`: array of GWT scenarios (may be empty)
41
+ - `edges`: relationships between elements
42
+
43
+ ## Example — full board analysis
44
+
45
+ ```bash
46
+ # 1. List slices
47
+ curl -s \
48
+ -H "x-token: $TOKEN" \
49
+ -H "x-board-id: $BOARD_ID" \
50
+ -H "x-user-id: analyze-existing-model" \
51
+ "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata/slices"
52
+
53
+ # 2. Fetch MODEL_CONTEXT nodes
54
+ curl -s \
55
+ -H "x-token: $TOKEN" \
56
+ -H "x-board-id: $BOARD_ID" \
57
+ -H "x-user-id: analyze-existing-model" \
58
+ "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=MODEL_CONTEXT"
59
+
60
+ # 3. Fetch full slice data for a context
61
+ curl -s \
62
+ -H "x-token: $TOKEN" \
63
+ -H "x-board-id: $BOARD_ID" \
64
+ -H "x-user-id: analyze-existing-model" \
65
+ "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata?contextName=Ordering"
66
+ ```
67
+
68
+ Replace `$TOKEN`, `$ORG_ID`, `$BOARD_ID`, and the context name with real values resolved from the `connect` skill.
@@ -44,37 +44,9 @@ The result gives you the ordered chain directly — save it as the chain used in
44
44
 
45
45
  ### Fallback (no MCP) — resolve both cells to nodes
46
46
 
47
- For each cell (target and source), resolve it to a node using the same cell-resolution strategy as the `examples` skill. Always fetch fresh:
47
+ For each cell (target and source), resolve it to a node using the exact same cell-resolution steps as the `examples` skill's "2c — Cell name" section (fetch chapters, fetch the chapter fresh to decode the grid, decode the cell name into a `CELL_ID`, then always fetch the cell live — `get_nodes` has no `cellId` filter) — see there for the full mechanics, substituting `x-user-id: attributes-skill`.
48
48
 
49
- 1. Fetch chapters:
50
- ```bash
51
- curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=CHAPTER" \
52
- -H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" -H "x-user-id: attributes-skill"
53
- ```
54
-
55
- If multiple chapters exist, ask the user which one to use.
56
-
57
- 2. Fetch the chapter fresh to decode the grid:
58
- ```bash
59
- curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID" \
60
- -H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" -H "x-user-id: attributes-skill"
61
- ```
62
-
63
- Decode the cell name:
64
- - Column letter(s) → 0-based index (A=0, B=1, … Z=25, AA=26, …)
65
- - Row number → 0-based index (1→0, 2→1, …)
66
- s- Find the matching column in `columns` and row in `rows`.
67
- - Compute: **`CELL_ID = row.id + "-" + column.id`** (cell IDs are always `<rowId>-<columnId>`).
68
-
69
- 3. Always fetch the cell live. No MCP equivalent: `get_nodes` only filters by `type`, not `cellId` — the alternative is `get_node` on the CHAPTER node, reading `meta.timelineData.cells` (a sparse array; an absent cell id means the cell is empty), but that reuses the same chapter fetch from step 2 rather than guaranteeing the freshest live state, so this curl call has no direct MCP replacement:
70
- ```bash
71
- curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=$CELL_ID" \
72
- -H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" -H "x-user-id: attributes-skill"
73
- ```
74
-
75
- Take the first non-CHAPTER result as the node for that cell.
76
-
77
- Save as `TARGET_NODE` and `SOURCE_NODE`.
49
+ Take the first non-CHAPTER result as the node for each cell. Save as `TARGET_NODE` and `SOURCE_NODE`.
78
50
 
79
51
  ---
80
52
 
@@ -94,11 +66,7 @@ An **inbound** edge is one where `edge.target === currentNode.id`. For each inbo
94
66
  mcp__eventmodelers__get_node { "boardId": "$BOARD_ID", "nodeId": "$EDGE_SOURCE_ID" }
95
67
  ```
96
68
 
97
- **Fallback (no MCP):**
98
- ```bash
99
- curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$EDGE_SOURCE_ID" \
100
- -H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" -H "x-user-id: attributes-skill"
101
- ```
69
+ **Fallback (no MCP):** see `references/api-fallback.md` — "3a — Use Node Edges".
102
70
 
103
71
  ### 3b — Column-based fallback (if no edges)
104
72
  If a node has no edges, use the chapter cell layout (already in memory) to find plausible inbound neighbours:
@@ -165,32 +133,7 @@ mcp__eventmodelers__submit_node_events {
165
133
  }
166
134
  ```
167
135
 
168
- **Fallback (no MCP)**build the payload with Python to avoid JSON escaping issues, then POST it:
169
-
170
- ```bash
171
- python3 - <<EOF > /tmp/attributes_payload.json
172
- import json, time, uuid
173
- payload = [{
174
- "id": str(uuid.uuid4()),
175
- "eventType": "node:changed",
176
- "nodeId": "<NODE_ID>",
177
- "boardId": "<BOARD_ID>",
178
- "timestamp": int(time.time() * 1000),
179
- "changedAttributes": ["meta.fields"],
180
- "meta": {
181
- "fields": <updated_fields_as_python_list>
182
- }
183
- }]
184
- print(json.dumps(payload))
185
- EOF
186
-
187
- curl -s -w "\n%{http_code}" -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/events" \
188
- -H "Content-Type: application/json" \
189
- -H "x-token: $TOKEN" \
190
- -H "x-board-id: $BOARD_ID" \
191
- -H "x-user-id: attributes-skill" \
192
- --data-binary @/tmp/attributes_payload.json
193
- ```
136
+ **Fallback (no MCP):** see `references/api-fallback.md` "Step 4 Apply the Change to Each Node in the Chain".
194
137
 
195
138
  Verify HTTP 200 before proceeding to the next node. If a node fails, report the error and stop.
196
139
 
@@ -0,0 +1,39 @@
1
+ # Attributes — curl Fallback Calls
2
+
3
+ Only needed when MCP is not connected. Every call below has an MCP equivalent in the main SKILL.md — always prefer that.
4
+
5
+ ## 3a — Use Node Edges
6
+
7
+ ```bash
8
+ curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$EDGE_SOURCE_ID" \
9
+ -H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" -H "x-user-id: attributes-skill"
10
+ ```
11
+
12
+ ## Step 4 — Apply the Change to Each Node in the Chain
13
+
14
+ Build the payload with Python to avoid JSON escaping issues, then POST it:
15
+
16
+ ```bash
17
+ python3 - <<EOF > /tmp/attributes_payload.json
18
+ import json, time, uuid
19
+ payload = [{
20
+ "id": str(uuid.uuid4()),
21
+ "eventType": "node:changed",
22
+ "nodeId": "<NODE_ID>",
23
+ "boardId": "<BOARD_ID>",
24
+ "timestamp": int(time.time() * 1000),
25
+ "changedAttributes": ["meta.fields"],
26
+ "meta": {
27
+ "fields": <updated_fields_as_python_list>
28
+ }
29
+ }]
30
+ print(json.dumps(payload))
31
+ EOF
32
+
33
+ curl -s -w "\n%{http_code}" -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/events" \
34
+ -H "Content-Type: application/json" \
35
+ -H "x-token: $TOKEN" \
36
+ -H "x-board-id: $BOARD_ID" \
37
+ -H "x-user-id: attributes-skill" \
38
+ --data-binary @/tmp/attributes_payload.json
39
+ ```
@@ -218,13 +218,7 @@ Discovered N screens across M flows:
218
218
  mcp__eventmodelers__create_chapter { "boardId": "<BOARD_ID>", "x": 0, "y": 0 }
219
219
  ```
220
220
 
221
- **Fallback (no MCP):**
222
- ```bash
223
- curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/chapters" \
224
- -H "x-token: $TOKEN" \
225
- -H "Content-Type: application/json" \
226
- -d '{"position":{"x":0,"y":0}}'
227
- ```
221
+ **Fallback (no MCP):** see `references/api-fallback.md` — "Step 5 — Create chapter".
228
222
 
229
223
  Extract `id` → `CHAPTER_ID` for this flow.
230
224
 
@@ -247,24 +241,7 @@ mcp__eventmodelers__submit_node_events {
247
241
  }
248
242
  ```
249
243
 
250
- **Fallback (no MCP):**
251
- ```bash
252
- curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/events" \
253
- -H "x-token: $TOKEN" \
254
- -H "x-board-id: $BOARD_ID" \
255
- -H "x-user-id: discover-storyboard" \
256
- -H "Content-Type: application/json" \
257
- -d '[{
258
- "id": "<uuid>",
259
- "eventType": "node:changed",
260
- "nodeId": "<CHAPTER_ID>",
261
- "boardId": "<BOARD_ID>",
262
- "timestamp": <NOW_MS>,
263
- "changedAttributes": ["meta.title"],
264
- "meta": { "type": "CHAPTER", "title": "<flow name>" },
265
- "node": { "id": "<CHAPTER_ID>", "data": {} }
266
- }]'
267
- ```
244
+ **Fallback (no MCP):** see `references/api-fallback.md` — "Step 5 — Update chapter title".
268
245
 
269
246
  Save `CHAPTER_ID` against each flow.
270
247
 
@@ -272,20 +249,16 @@ Save `CHAPTER_ID` against each flow.
272
249
 
273
250
  ## Step 6 — Fetch chapter grid and build column queue
274
251
 
275
- For each chapter, fetch its current grid state.
252
+ For each chapter, fetch its current grid state — `projection: "cells"` returns just `{rows, columns, cells}`, not the whole chapter node.
276
253
 
277
254
  **Prefer MCP:**
278
255
  ```
279
- mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<CHAPTER_ID>" }
256
+ mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<CHAPTER_ID>", "projection": "cells" }
280
257
  ```
281
258
 
282
- **Fallback (no MCP):**
283
- ```bash
284
- curl -s -H "x-token: $TOKEN" \
285
- "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID"
286
- ```
259
+ **Fallback (no MCP):** see `references/api-fallback.md` — "Step 6 — Fetch chapter grid".
287
260
 
288
- From `meta.timelineData`:
261
+ From the result (`rows`/`columns`/`cells` directly via MCP, or `meta.timelineData` via the REST fallback):
289
262
  - `rows` — find the row with `type === "actor"` → save its `id` as `actorRowId` and its 0-based position in `rows` as `actorRowIndex`
290
263
  - `columns` — ordered list; build an empty-column queue, remembering each entry's 0-based position in `columns` as its `columnIndex`
291
264
 
@@ -311,16 +284,7 @@ mcp__eventmodelers__add_column { "boardId": "<BOARD_ID>", "timelineId": "<CHAPTE
311
284
  ```
312
285
  The result's `index` field is the new column's `columnIndex` (use it directly for `CELL_NAME` — no need to recompute from the full column list).
313
286
 
314
- **Fallback (no MCP):**
315
- ```bash
316
- curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/timelines/$CHAPTER_ID/columns" \
317
- -H "x-token: $TOKEN" \
318
- -H "x-board-id: $BOARD_ID" \
319
- -H "x-user-id: discover-storyboard" \
320
- -H "Content-Type: application/json" \
321
- -d '{}'
322
- ```
323
- Response includes `{ columnId, index, totalColumns }` — `index` is the new `columnIndex`.
287
+ **Fallback (no MCP):** see `references/api-fallback.md` — "Step 7a — Create column".
324
288
 
325
289
  Extract `columnId`. Compute `CELL_ID = actorRowId + "-" + columnId` and `CELL_NAME` per the convention above.
326
290
 
@@ -336,7 +300,7 @@ This step must create the node and attach its content (HTML pages, or the screen
336
300
 
337
301
  **Default path — `renderMode == "html"` (use unless the user explicitly asked for screenshots/images in Step 2):**
338
302
 
339
- Reconstruct the captured screen as a real HTML/CSS fragment from `screen.capturedMarkup`, following the `html-screen` skill's conventions: full-size markup (16px body text, generous padding), one self-contained fragment per page (no `<html>`/`<head>`/`<body>` wrapper), no `<script>`/inline handlers, Bulma CSS classes (`title`, `button`, `is-primary`, `field`/`control`/`input`, tables, tags for status badges, etc.). Reproduce the real layout, labels, form fields, buttons, and any live data/status values seen on the actual page — this is a faithful reconstruction of the discovered screen, not a generic mockup.
303
+ Reconstruct the captured screen as a real HTML/CSS fragment from `screen.capturedMarkup`, following the `html-screen` skill's page-design conventions (Step 3) full-size markup, one self-contained fragment per page, no script/inline handlers, Bulma classes. Reproduce the real layout, labels, form fields, buttons, and any live data/status values seen on the actual page — this is a faithful reconstruction of the discovered screen, not a generic mockup.
340
304
 
341
305
  ```
342
306
  mcp__eventmodelers__create_screen {
@@ -367,15 +331,7 @@ mcp__eventmodelers__create_screen {
367
331
  }
368
332
  ```
369
333
 
370
- **Fallback (no MCP)** the same atomic operation via the `image-nodes` endpoint (not the plain `images/:id` endpoint, which only updates an existing node's image and does not place it) — image path only:
371
- ```bash
372
- curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/image-nodes/$SCREEN_NODE_ID" \
373
- -H "x-token: $TOKEN" \
374
- -F "file=@<screen.filepath>" \
375
- -F "chapterId=$CHAPTER_ID" \
376
- -F "cellName=$CELL_NAME"
377
- ```
378
- For the HTML path with no MCP, use the `html-screen-nodes` endpoint per the `html-screen` skill's fallback mechanics instead.
334
+ **Fallback (no MCP):** see `references/api-fallback.md` "Step 7b Upload image (fallback)".
379
335
 
380
336
  Response: `204`/success on success. Log failures in the final report but continue to the next screen — do not stop the entire run.
381
337
 
@@ -0,0 +1,63 @@
1
+ # Discover Storyboard — curl Fallback Calls
2
+
3
+ Only needed when MCP is not connected. Every call below has an MCP equivalent in the main SKILL.md — always prefer that.
4
+
5
+ ## Step 5 — Create chapter
6
+
7
+ ```bash
8
+ curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/chapters" \
9
+ -H "x-token: $TOKEN" \
10
+ -H "Content-Type: application/json" \
11
+ -d '{"position":{"x":0,"y":0}}'
12
+ ```
13
+
14
+ ## Step 5 — Update chapter title
15
+
16
+ ```bash
17
+ curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/events" \
18
+ -H "x-token: $TOKEN" \
19
+ -H "x-board-id: $BOARD_ID" \
20
+ -H "x-user-id: discover-storyboard" \
21
+ -H "Content-Type: application/json" \
22
+ -d '[{
23
+ "id": "<uuid>",
24
+ "eventType": "node:changed",
25
+ "nodeId": "<CHAPTER_ID>",
26
+ "boardId": "<BOARD_ID>",
27
+ "timestamp": <NOW_MS>,
28
+ "changedAttributes": ["meta.title"],
29
+ "meta": { "type": "CHAPTER", "title": "<flow name>" },
30
+ "node": { "id": "<CHAPTER_ID>", "data": {} }
31
+ }]'
32
+ ```
33
+
34
+ ## Step 6 — Fetch chapter grid
35
+
36
+ ```bash
37
+ curl -s -H "x-token: $TOKEN" \
38
+ "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID"
39
+ ```
40
+
41
+ ## Step 7a — Create column
42
+
43
+ ```bash
44
+ curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/timelines/$CHAPTER_ID/columns" \
45
+ -H "x-token: $TOKEN" \
46
+ -H "x-board-id: $BOARD_ID" \
47
+ -H "x-user-id: discover-storyboard" \
48
+ -H "Content-Type: application/json" \
49
+ -d '{}'
50
+ ```
51
+ Response includes `{ columnId, index, totalColumns }` — `index` is the new `columnIndex`.
52
+
53
+ ## Step 7b — Upload image (fallback)
54
+
55
+ The same atomic operation via the `image-nodes` endpoint (not the plain `images/:id` endpoint, which only updates an existing node's image and does not place it) — image path only:
56
+ ```bash
57
+ curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/image-nodes/$SCREEN_NODE_ID" \
58
+ -H "x-token: $TOKEN" \
59
+ -F "file=@<screen.filepath>" \
60
+ -F "chapterId=$CHAPTER_ID" \
61
+ -F "cellName=$CELL_NAME"
62
+ ```
63
+ For the HTML path with no MCP, use the `html-screen-nodes` endpoint per the `html-screen` skill's fallback mechanics instead.