@eventmodelers/cli 1.0.90 → 1.0.92
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 +2 -0
- package/RELEASE_NOTES.md +3 -0
- package/cli.js +308 -23
- package/lib/update-check.js +67 -0
- package/package.json +4 -1
- package/shared/build-kit/lib/adapters/pocketbase-realtime-adapter.js +11 -3
- package/shared/build-kit/lib/adapters/realtime-adapter.js +3 -1
- package/shared/build-kit/lib/adapters/supabase-realtime-adapter.js +14 -3
- package/shared/build-kit/lib/ralph.js +136 -59
- package/shared/build-kit/lib/redact.js +36 -0
- package/shared/build-kit/lib/turn.js +56 -20
- package/shared/build-kit/ralph-claude.js +7 -19
- package/shared/build-kit/ralph-exec.js +5 -9
- package/shared/build-kit/realtime-agent.js +4 -2
- package/shared/skills/connect/SKILL.md +14 -9
- package/shared/skills/learn-eventmodelers-api/SKILL.md +61 -24
- package/shared/skills/load-slice/SKILL.md +3 -1
- package/stacks/modeling-kit/templates/.claude/skills/add-next-slice/SKILL.md +4 -4
- package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/SKILL.md +18 -9
- package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/references/api-fallback.md +31 -11
- package/stacks/modeling-kit/templates/.claude/skills/attributes/SKILL.md +16 -13
- package/stacks/modeling-kit/templates/.claude/skills/attributes/references/api-fallback.md +3 -1
- package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/SKILL.md +6 -6
- package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/references/api-fallback.md +3 -1
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/SKILL.md +4 -4
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/references/api-fallback.md +4 -4
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/SKILL.md +8 -12
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/references/api-fallback.md +16 -6
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +3 -3
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/references/api-fallback.md +1 -1
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/SKILL.md +2 -2
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/references/api-fallback.md +4 -4
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md +4 -4
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/references/api-fallback.md +4 -4
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/SKILL.md +3 -3
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/references/api-fallback.md +3 -1
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/SKILL.md +2 -2
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/references/api-fallback.md +1 -1
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/SKILL.md +3 -3
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/api-fallback.md +12 -4
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/SKILL.md +7 -6
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/references/api-fallback.md +4 -3
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/SKILL.md +22 -6
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/references/api-fallback.md +12 -3
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/SKILL.md +18 -6
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/references/api-fallback.md +12 -3
- package/stacks/modeling-kit/templates/.claude/skills/examples/SKILL.md +2 -2
- package/stacks/modeling-kit/templates/.claude/skills/examples/references/api-fallback.md +20 -14
- package/stacks/modeling-kit/templates/.claude/skills/place-element/SKILL.md +12 -12
- package/stacks/modeling-kit/templates/.claude/skills/place-element/references/api-fallback.md +6 -4
- package/stacks/modeling-kit/templates/.claude/skills/storyboard/SKILL.md +4 -4
- package/stacks/modeling-kit/templates/.claude/skills/storyboard/references/api-fallback.md +2 -2
- package/stacks/modeling-kit/templates/.claude/skills/timeline/SKILL.md +10 -10
- package/stacks/modeling-kit/templates/.claude/skills/timeline/references/api-fallback.md +4 -4
- package/stacks/modeling-kit/templates/.claude/skills/wdyt/SKILL.md +11 -24
- package/stacks/modeling-kit/templates/kit/AGENTS.md +3 -3
- package/stacks/modeling-kit/templates/kit/CLAUDE-STANDALONE.md +56 -10
- package/stacks/modeling-kit/templates/kit/CLAUDE.md +110 -44
- package/stacks/react/templates/build-kit/lib/backend-prompt.md +106 -102
- package/stacks/react/templates/build-kit/lib/prompt.md +102 -106
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/SKILL.md
CHANGED
|
@@ -90,12 +90,12 @@ Solution: Every event includes timestamp
|
|
|
90
90
|
|
|
91
91
|
## Board Integration
|
|
92
92
|
|
|
93
|
-
Before starting the analysis, read existing screen nodes from the board to avoid designing screens that already exist. Screens created by this skill default to HTML_SCREEN, but older boards may still have plain SCREEN (sketch) nodes — check both types:
|
|
93
|
+
Before starting the analysis, read existing screen nodes from the board to avoid designing screens that already exist. Screens created by this skill default to HTML_SCREEN, but older boards may still have plain SCREEN (sketch) nodes — check both types. Titles and field names are all this dedupe needs, so use `projection: "line"` (no HTML bodies); read a screen in full with `get_node` only if you must inspect its design:
|
|
94
94
|
|
|
95
95
|
**Prefer MCP:**
|
|
96
96
|
```
|
|
97
|
-
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "HTML_SCREEN" }
|
|
98
|
-
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "SCREEN" }
|
|
97
|
+
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "HTML_SCREEN", "projection": "line" }
|
|
98
|
+
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "SCREEN", "projection": "line" }
|
|
99
99
|
```
|
|
100
100
|
|
|
101
101
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Board Integration — Check existing screen nodes".
|
|
@@ -219,8 +219,9 @@ Every screen node requires rendered content. **HTML_SCREEN (via the `html-screen
|
|
|
219
219
|
1. Determine the target column (same column as the event/command for a command/input screen, OR the same column as the read model for a view/output screen — one column to the right only if that shared column isn't available).
|
|
220
220
|
2. `actorRowId = roleLaneMap[<this screen's role>]` — the map was already resolved once for the whole chapter; do not re-fetch the chapter per screen. If this screen's role is genuinely new (wasn't in the original Role Catalog), resolve/create its lane now the same way (see above) and add it to the map before continuing.
|
|
221
221
|
3. `cellId = actorRowId + "-" + columnId`
|
|
222
|
+
4. For MCP `create_screen`, which takes only `cellName`: never derive it from array positions — read it from `get_board_outline` for that chapter (call it once if you don't already hold it): the column letter is the target column's `letter` there, the row number is the target lane's 1-based position in its `lanes` list (lanes are listed top to bottom, so the first lane is row 1) (e.g. column `letter: "E"`, this role's actor lane second in `lanes` → `E2`).
|
|
222
223
|
|
|
223
|
-
**Step B (default) — Create the HTML_SCREEN node and render it in one atomic call.** Use `create_screen` with `contentType: "html"` — this creates the node, places it in
|
|
224
|
+
**Step B (default) — Create the HTML_SCREEN node and render it in one atomic call.** Use `create_screen` with `contentType: "html"` — this creates the node, places it in that cell, and renders its pages together, so there is no window where the node exists without content:
|
|
224
225
|
|
|
225
226
|
**Prefer MCP:**
|
|
226
227
|
```
|
|
@@ -230,7 +231,7 @@ mcp__eventmodelers__create_screen {
|
|
|
230
231
|
"contentType": "html",
|
|
231
232
|
"nodeId": "<node-uuid>",
|
|
232
233
|
"chapterId": "<CHAPTER_ID>",
|
|
233
|
-
"
|
|
234
|
+
"cellName": "<cellName, e.g. E2>",
|
|
234
235
|
"title": "<Screen Title>",
|
|
235
236
|
"pages": ["<div>...</div>"],
|
|
236
237
|
"description": "<concise description of what this screen shows>",
|
|
@@ -394,7 +395,7 @@ Older versions of this skill wrote the storyboard as a markdown document (swimla
|
|
|
394
395
|
- [ ] **Every human role from the Role Catalog has at least one swimlane**
|
|
395
396
|
- [ ] **Every human-role swimlane is labeled with the role name from the catalog**
|
|
396
397
|
- [ ] **Swimlanes organized by actor/system in the narrative report**
|
|
397
|
-
- [ ] **Every human role's swimlane is a real, distinct `actor`-type lane on the board (`
|
|
398
|
+
- [ ] **Every human role's swimlane is a real, distinct `actor`-type lane on the board (the chapter's `rows`), not just a grouping in the markdown report** — no two different human roles share the same `actorRowId`
|
|
398
399
|
- [ ] **No system actor / processor has been given its own labeled actor lane** — every AUTOMATION node sits in the chapter's default actor lane, never a lane fabricated to mimic a human role's
|
|
399
400
|
- [ ] **Human role screens clearly separated from processor screens**
|
|
400
401
|
- [ ] **Processor todo list pattern shown for automated systems**
|
|
@@ -6,16 +6,17 @@ Only needed when MCP is not connected. Every call below has an MCP equivalent in
|
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
curl -s -H "x-token: $TOKEN" \
|
|
9
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=HTML_SCREEN"
|
|
9
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=HTML_SCREEN&projection=line"
|
|
10
10
|
curl -s -H "x-token: $TOKEN" \
|
|
11
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=SCREEN"
|
|
11
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=SCREEN&projection=line"
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
## Resolve One Actor Lane Per Human Role — Step 1: Fetch the chapter's actor rows
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
17
|
curl -s -H "x-token: $TOKEN" \
|
|
18
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID"
|
|
18
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID?projection=cells"
|
|
19
|
+
# → {rows, columns, cells} directly — collect every row where type === "actor"
|
|
19
20
|
```
|
|
20
21
|
|
|
21
22
|
## Resolve One Actor Lane Per Human Role — Step 3: Create a new actor lane
|
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/SKILL.md
CHANGED
|
@@ -27,13 +27,29 @@ The source can be also determined by looking at the defined Scenarios. Are all S
|
|
|
27
27
|
|
|
28
28
|
Before starting, read the current board state to validate what is actually on the board.
|
|
29
29
|
|
|
30
|
-
Prefer MCP
|
|
30
|
+
Prefer MCP (no header wiring needed, auth resolves from the connected session). Pick the read by what each check needs:
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "
|
|
36
|
-
|
|
32
|
+
- **Naming, past tense, duplicates, attribute names** — only titles, types and field names are needed, so call `get_nodes` once per type with the `line` projection (`{id, type, title, fields}`, `fields` = attribute names):
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "EVENT", "projection": "line" }
|
|
36
|
+
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "COMMAND", "projection": "line" }
|
|
37
|
+
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "READMODEL", "projection": "line" }
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
- **Field-source traceability** (every command field comes from a connected read model or is marked generated) — needs field bodies and dependencies, so use the slice data `fields` projection per context:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
mcp__eventmodelers__get_slice_data { "boardId": "$BOARD_ID", "contextName": "<CONTEXT_NAME>", "projection": "fields", "format": "toon" }
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- **Scenario coverage** — use the `specs` projection (given/when/then per slice, no elements):
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
mcp__eventmodelers__get_slice_data { "boardId": "$BOARD_ID", "contextName": "<CONTEXT_NAME>", "projection": "specs", "format": "toon" }
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`specs` (like `outline`) only encodes as `json`, `yaml` or `toon`. Context names: `get_nodes { "type": "MODEL_CONTEXT", "projection": "line" }`, or a timeline name.
|
|
37
53
|
|
|
38
54
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Board Context".
|
|
39
55
|
|
|
@@ -5,10 +5,19 @@ Only needed when MCP is not connected. Every call below has an MCP equivalent in
|
|
|
5
5
|
## Board Context
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
+
# Naming / duplicates / attribute names — compact per-type lists
|
|
8
9
|
curl -s -H "x-token: $TOKEN" \
|
|
9
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=EVENT"
|
|
10
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=EVENT&projection=line"
|
|
10
11
|
curl -s -H "x-token: $TOKEN" \
|
|
11
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=COMMAND"
|
|
12
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=COMMAND&projection=line"
|
|
12
13
|
curl -s -H "x-token: $TOKEN" \
|
|
13
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=READMODEL"
|
|
14
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=READMODEL&projection=line"
|
|
15
|
+
|
|
16
|
+
# Field-source traceability — fields + dependencies per slice
|
|
17
|
+
curl -s -H "x-token: $TOKEN" \
|
|
18
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata?contextName=<CONTEXT_NAME>&projection=fields&format=toon"
|
|
19
|
+
|
|
20
|
+
# Scenario coverage — specifications per slice (format must be json|yaml|toon)
|
|
21
|
+
curl -s -H "x-token: $TOKEN" \
|
|
22
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata?contextName=<CONTEXT_NAME>&projection=specs&format=toon"
|
|
14
23
|
```
|
|
@@ -38,13 +38,25 @@ Prefer `mcp__eventmodelers__*` tools when available (registered by the `connect`
|
|
|
38
38
|
|
|
39
39
|
Read the current board state before running the checklist:
|
|
40
40
|
|
|
41
|
-
**Prefer MCP
|
|
41
|
+
**Prefer MCP** — pick the read by what the phase checks:
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "
|
|
47
|
-
|
|
43
|
+
- **Name/type checks** (Phase 1.1 timeline ownership by name, Phase 2.3 past tense, duplicate names) — `get_nodes` once per type with the `line` projection (`{id, type, title, fields}`, `fields` = attribute names only):
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "EVENT", "projection": "line" }
|
|
47
|
+
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "COMMAND", "projection": "line" }
|
|
48
|
+
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "READMODEL", "projection": "line" }
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
- **Field-level checks** (Phase 2.1/2.2 computed or mutable event data, Phase 3 read-model sources, Phase 5 traceability, Phase 7 structural shapes via dependencies) — the slice data `fields` projection per context: elements with fields and dependencies, no specs/comments:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
mcp__eventmodelers__get_slice_data { "boardId": "<BOARD_ID>", "contextName": "<CONTEXT_NAME>", "projection": "fields", "format": "toon" }
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- **Business rules / scenarios** (Phase 4) — add `"projection": "specs"` (format `json`, `yaml` or `toon` only).
|
|
58
|
+
|
|
59
|
+
Context names come from `get_nodes { "type": "MODEL_CONTEXT", "projection": "line" }` (or use a timeline name).
|
|
48
60
|
|
|
49
61
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Board Context".
|
|
50
62
|
|
|
@@ -5,10 +5,19 @@ Only needed when MCP is not connected. Every call below has an MCP equivalent in
|
|
|
5
5
|
## Board Context
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
+
# Name/type checks — compact per-type lists
|
|
8
9
|
curl -s -H "x-token: $TOKEN" \
|
|
9
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=EVENT"
|
|
10
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=EVENT&projection=line"
|
|
10
11
|
curl -s -H "x-token: $TOKEN" \
|
|
11
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=COMMAND"
|
|
12
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=COMMAND&projection=line"
|
|
12
13
|
curl -s -H "x-token: $TOKEN" \
|
|
13
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=READMODEL"
|
|
14
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=READMODEL&projection=line"
|
|
15
|
+
|
|
16
|
+
# Field-level checks — fields + dependencies per slice
|
|
17
|
+
curl -s -H "x-token: $TOKEN" \
|
|
18
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata?contextName=<CONTEXT_NAME>&projection=fields&format=toon"
|
|
19
|
+
|
|
20
|
+
# Business rules / scenarios (format must be json|yaml|toon)
|
|
21
|
+
curl -s -H "x-token: $TOKEN" \
|
|
22
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata?contextName=<CONTEXT_NAME>&projection=specs&format=toon"
|
|
14
23
|
```
|
|
@@ -42,8 +42,8 @@ A run that opens each element with its own `get_node` to "read existing examples
|
|
|
42
42
|
Resolve `target` to a node, with whichever identifier it is:
|
|
43
43
|
|
|
44
44
|
- `target` is a UUID → `mcp__eventmodelers__get_node { "boardId": "$BOARD_ID", "nodeId": "<target>" }`
|
|
45
|
-
- `target` is a name → `mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "name": "<target>" }` — a partial, case-insensitive title match; prefer an exact title, and if several elements still match, list them and ask the user to pick one
|
|
46
|
-
- `target` is a cell name (e.g. `B3`) → `
|
|
45
|
+
- `target` is a name → `mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "name": "<target>", "projection": "line" }` — a partial, case-insensitive title match; `line` is enough to pick (ids/titles/types only); prefer an exact title, and if several elements still match, list them and ask the user to pick one. Then read the chosen node in full with `get_node` (Step 4 needs its fields' types and examples).
|
|
46
|
+
- `target` is a cell name (e.g. `B3`) → `mcp__eventmodelers__get_board_outline { "boardId": "$BOARD_ID", "chapterId": "$CHAPTER_ID" }` (the only read that reports `cellName`) and take the node whose `cellName` is `target`, then read it in full by its id with `get_node` (if multiple chapters exist, find the chapter first with `get_nodes { "type": "CHAPTER", "projection": "line" }` and ask the user if ambiguous). If you already did the full chapter read from Step 1b, take the node by that id from there instead of `get_node`. Never construct a cell address yourself — only use one you were given or read back from `get_board_outline`; from then on address the element by its node id.
|
|
47
47
|
|
|
48
48
|
Then continue with Step 3 (linked elements) — `mcp__eventmodelers__get_connected_nodes { "boardId": "$BOARD_ID", "nodeId": "<resolved id>", "includeFields": true }` returns the neighbours and their fields in one call.
|
|
49
49
|
|
|
@@ -15,15 +15,15 @@ curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$target" \
|
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
### 2b — Name search
|
|
18
|
-
If `target` is not a UUID,
|
|
18
|
+
If `target` is not a UUID, search by title with the `name` filter (partial, case-insensitive) — `projection=line` returns just `{id, type, title, fields}` per match, enough to pick one:
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes" \
|
|
22
|
-
-
|
|
23
|
-
|
|
21
|
+
curl -s -G "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes" \
|
|
22
|
+
--data-urlencode "name=$target" --data-urlencode "projection=line" \
|
|
23
|
+
-H "x-user-id: examples-skill"
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
Pick the best match (exact title match preferred; case-insensitive). If multiple matches exist, list them and ask the user to pick one.
|
|
26
|
+
Pick the best match (exact `title` match preferred; case-insensitive). If multiple matches exist, list them and ask the user to pick one. Then fetch the chosen node in full as in 2a — Step 4 needs its `meta.fields` with types and examples.
|
|
27
27
|
|
|
28
28
|
### 2c — Cell name (spreadsheet-style, e.g. "B3")
|
|
29
29
|
If `target` matches the pattern `[A-Z]+[0-9]+`:
|
|
@@ -31,7 +31,7 @@ If `target` matches the pattern `[A-Z]+[0-9]+`:
|
|
|
31
31
|
1. Fetch all chapters on the board to find the relevant timeline:
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=CHAPTER" \
|
|
34
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=CHAPTER&projection=line" \
|
|
35
35
|
-H "x-user-id: examples-skill"
|
|
36
36
|
```
|
|
37
37
|
|
|
@@ -40,20 +40,20 @@ If multiple chapters exist, ask the user which one to use.
|
|
|
40
40
|
2. Fetch the chapter **fresh** to decode the grid — never use cached chapter data, as cells are updated frequently:
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID" \
|
|
43
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID?projection=cells" \
|
|
44
44
|
-H "x-user-id: examples-skill"
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
From `meta.timelineData
|
|
47
|
+
From the returned `{rows, columns, cells}` (not wrapped in `meta.timelineData`), decode the cell name into a `cellId`:
|
|
48
48
|
- Column letter(s) → 0-based column index (A=0, B=1, … Z=25, AA=26, …)
|
|
49
49
|
- Row digit → 0-based row index (1→0, 2→1, …)
|
|
50
50
|
- Find the matching column in `columns` and the matching row in `rows`.
|
|
51
51
|
- Compute: **`CELL_ID = row.id + "-" + column.id`** (cell IDs are always `<rowId>-<columnId>`).
|
|
52
52
|
|
|
53
|
-
3. **Always fetch the cell live** to get the current node list — do not rely on the `nodeId` in the chapter's cell data, as it may be stale. No MCP equivalent: `get_nodes`
|
|
53
|
+
3. **Always fetch the cell live** to get the current node list — do not rely on the `nodeId` in the chapter's cell data, as it may be stale. No MCP equivalent: `get_nodes` filters by `type`, `name`, `chapterId` and `nodeIds`, but not `cellId`. The REST `cellId` filter requires `timelineId`:
|
|
54
54
|
|
|
55
55
|
```bash
|
|
56
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=$CELL_ID" \
|
|
56
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=$CELL_ID&timelineId=$CHAPTER_ID" \
|
|
57
57
|
-H "x-user-id: examples-skill"
|
|
58
58
|
```
|
|
59
59
|
|
|
@@ -61,21 +61,27 @@ Use the first non-CHAPTER result (filter out CHAPTER type).
|
|
|
61
61
|
|
|
62
62
|
If no element is found after all strategies, stop and tell the user what was tried.
|
|
63
63
|
|
|
64
|
-
Save the resolved node as `TARGET_NODE` (full JSON including `id
|
|
64
|
+
Save the resolved node as `TARGET_NODE` (full JSON including `id` and `meta`).
|
|
65
65
|
|
|
66
66
|
## Step 3 — Load linked elements for context
|
|
67
67
|
|
|
68
68
|
Collect nearby elements to understand the domain context and generate consistent examples. **Never fetch all board nodes.** Only fetch specific nodes you already have IDs for.
|
|
69
69
|
|
|
70
70
|
### 3a — Nodes from edges
|
|
71
|
-
|
|
71
|
+
Read the target's connections only (`projection=edges` → `{nodeId, edges}`, no node content; the full node record carries no edges):
|
|
72
72
|
|
|
73
73
|
```bash
|
|
74
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes
|
|
74
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$TARGET_ID?projection=edges" \
|
|
75
75
|
-H "x-user-id: examples-skill"
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
If `edges` is non-empty, fetch every connected node (the `source`/`target` that isn't `TARGET_ID`) in full — Step 3c needs their field examples — in one call:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/query" \
|
|
82
|
+
-H "Content-Type: application/json" -H "x-user-id: examples-skill" \
|
|
83
|
+
-d '{"ids": ["<EDGE_NODE_ID>", "..."]}'
|
|
84
|
+
```
|
|
79
85
|
|
|
80
86
|
### 3b — Nodes from the same column (cell-based resolution only)
|
|
81
87
|
If you resolved `TARGET_NODE` via a cell name (Step 2c), you already have the full chapter `cells` array in memory. Use it — **no extra API call needed**:
|
|
@@ -64,10 +64,10 @@ This tool finds or creates an empty cell in the correct lane and places the node
|
|
|
64
64
|
|
|
65
65
|
## Step 2 — Resolve the timeline
|
|
66
66
|
|
|
67
|
-
**Prefer MCP
|
|
67
|
+
**Prefer MCP** — ids and titles are all this step needs, so use `projection: "line"` (without it every chapter's full grid comes back):
|
|
68
68
|
|
|
69
69
|
```
|
|
70
|
-
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "CHAPTER" }
|
|
70
|
+
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "CHAPTER", "projection": "line" }
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Step 2 — Discover chapters". If `timelineId` is not provided, this discovers chapters on the board.
|
|
@@ -90,7 +90,7 @@ mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<TIMELINE_ID>
|
|
|
90
90
|
|
|
91
91
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Step 3 — Fetch the chapter node".
|
|
92
92
|
|
|
93
|
-
Read `columns` (ordered array of column objects with `id` and `index`) and `cells` (from the result
|
|
93
|
+
Read `columns` (ordered array of column objects with `id` and `index`) and `cells` (directly from the result — MCP and the REST fallback's `?projection=cells` return the same `{rows, columns, cells}` shape).
|
|
94
94
|
|
|
95
95
|
Then resolve `position`:
|
|
96
96
|
|
|
@@ -191,7 +191,7 @@ Save `columnId` from the response.
|
|
|
191
191
|
|
|
192
192
|
## Step 6 — Compute the target cell ID and check availability
|
|
193
193
|
|
|
194
|
-
Using the `
|
|
194
|
+
Using the `{rows, columns, cells}` grid already fetched in Step 3 (re-fetch if a column was just created):
|
|
195
195
|
|
|
196
196
|
- Find the row in `rows` whose `type` matches the target lane (`swimlane`, `interaction`, or `actor`).
|
|
197
197
|
- Compute the cell ID directly: **`CELL_ID = targetRow.id + "-" + columnId`**
|
|
@@ -208,7 +208,7 @@ Cell IDs are always `<rowId>-<columnId>` — no cell array search needed.
|
|
|
208
208
|
|
|
209
209
|
**Fastest path**: use the `cells` array you already fetched in Step 3 via `get_node` (`projection: "cells"`) on the chapter/timeline node — `cells` is a sparse array, so a `nodeId` absent from the entry for `CELL_ID` means the cell is empty. Also scan it for the column-wide conflict from the rule above: any entry with `colId === columnId` and `nodeType === elementType` (for COMMAND/READMODEL/SCREEN/AUTOMATION) means the column is occupied even though `CELL_ID` itself is free. This avoids an extra round-trip, but can be stale if a column was just created in Step 5 — re-fetch first in that case.
|
|
210
210
|
|
|
211
|
-
**No direct MCP equivalent** for querying by cell/column: `get_nodes`
|
|
211
|
+
**No direct MCP equivalent** for querying by cell/column: `get_nodes` filters by `type`, `name`, `chapterId` and `nodeIds`, but has no `cellId`/`colId` filter. The REST API does support it directly though — `GET /nodes` takes `cellId` **or** `colId`, both requiring `timelineId`:
|
|
212
212
|
|
|
213
213
|
```bash
|
|
214
214
|
# Same-cell occupancy — returns [] if empty, or [nodeRecord] if occupied
|
|
@@ -218,7 +218,7 @@ curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=$CELL_ID&timeli
|
|
|
218
218
|
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?colId=$COLUMN_ID&timelineId=$TIMELINE_ID"
|
|
219
219
|
```
|
|
220
220
|
|
|
221
|
-
Use this REST call (see the curl fallback below) when you don't already have fresh
|
|
221
|
+
Use this REST call (see the curl fallback below) when you don't already have a fresh grid loaded, instead of re-fetching the chapter just to check occupancy.
|
|
222
222
|
|
|
223
223
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Step 6 — Check cell occupancy".
|
|
224
224
|
|
|
@@ -281,7 +281,7 @@ mcp__eventmodelers__create_screen {
|
|
|
281
281
|
"contentType": "html",
|
|
282
282
|
"nodeId": "<node-uuid>",
|
|
283
283
|
"chapterId": "<TIMELINE_ID>",
|
|
284
|
-
"
|
|
284
|
+
"cellName": "<CELL_NAME>",
|
|
285
285
|
"title": "<title>",
|
|
286
286
|
"pages": ["<div>...</div>"],
|
|
287
287
|
"description": "<title — what this screen shows>"
|
|
@@ -302,7 +302,7 @@ mcp__eventmodelers__create_screen {
|
|
|
302
302
|
"contentType": "sketch",
|
|
303
303
|
"nodeId": "<node-uuid>",
|
|
304
304
|
"chapterId": "<TIMELINE_ID>",
|
|
305
|
-
"
|
|
305
|
+
"cellName": "<CELL_NAME>",
|
|
306
306
|
"title": "<title>",
|
|
307
307
|
"elements": [...],
|
|
308
308
|
"description": "<title — what this screen shows>"
|
|
@@ -312,7 +312,7 @@ mcp__eventmodelers__create_screen {
|
|
|
312
312
|
|
|
313
313
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Step 7a — SCREEN: create and render (sketch path, explicit request only)".
|
|
314
314
|
|
|
315
|
-
|
|
315
|
+
MCP `create_screen` takes only `cellName`: pass `CELL_NAME` from Step 1's fast path as-is, or, on the normal path, don't convert Step 6's `CELL_ID` from array positions — read it from `get_board_outline` for that chapter (call it once if you don't already hold it): the column letter is the target column's `letter` there, the row number is the target lane's 1-based position in its `lanes` list (lanes are listed top to bottom, so the first lane is row 1) (e.g. column `letter: "C"`, the target lane second in `lanes` → `C2`). The curl fallback still accepts `cellId`. Expect success (MCP: `created: true`; curl: `204`). On failure, read the validation error, fix the payload, and retry once. Then skip the rest of Step 7 and go to Step 8.
|
|
316
316
|
|
|
317
317
|
### Step 7b — All other element types
|
|
318
318
|
|
|
@@ -372,14 +372,14 @@ Response: `{ "hashes": { "<event-id>": "<hash>" } }` — keyed by the `id` you s
|
|
|
372
372
|
|
|
373
373
|
**A command is never issued by more than one thing.** The server's fire-and-forget auto-connect (`learn-eventmodelers-api` §3) already enforces this — it skips wiring the previous column's SCREEN/AUTOMATION into a COMMAND that already has an inbound trigger, whether that trigger came from the COMMAND's own column or from a pre-existing edge in the DB. Still, run this check whenever `elementType` is `SCREEN`, `AUTOMATION`, or `COMMAND` as a sanity check — e.g. a manual `set_connection` call, or an edge created before this guard existed, can still leave a COMMAND with two issuers.
|
|
374
374
|
|
|
375
|
-
After placing, resolve the relevant COMMAND node (the one just placed, or the one in the same/adjacent column as the SCREEN/AUTOMATION just placed) and inspect its edges:
|
|
375
|
+
After placing, resolve the relevant COMMAND node (the one just placed, or the one in the same/adjacent column as the SCREEN/AUTOMATION just placed) and inspect its edges — only its connections are needed, so use `projection: "edges"` (the full node record carries no edges):
|
|
376
376
|
|
|
377
377
|
**Prefer MCP:**
|
|
378
378
|
```
|
|
379
|
-
mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<COMMAND_NODE_ID>" }
|
|
379
|
+
mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<COMMAND_NODE_ID>", "projection": "edges" }
|
|
380
380
|
```
|
|
381
381
|
|
|
382
|
-
|
|
382
|
+
Take the inbound edges (`target === COMMAND_NODE_ID`) and resolve their source types in one call — only `type` is needed, so `get_nodes { "boardId": "<BOARD_ID>", "nodeIds": [<every inbound source id>], "projection": "line" }`. Count the inbound edges whose source node is type `SCREEN`, `HTML_SCREEN` or `AUTOMATION`.
|
|
383
383
|
|
|
384
384
|
- **0 or 1 such edge** → fine, nothing to do.
|
|
385
385
|
- **2 or more** → keep the edge whose source sits in the COMMAND's own column (the deliberate, same-slice issuer) and remove every other one:
|
package/stacks/modeling-kit/templates/.claude/skills/place-element/references/api-fallback.md
CHANGED
|
@@ -5,15 +5,17 @@ Only needed when MCP is not connected. Every call below has an MCP equivalent in
|
|
|
5
5
|
## Step 2 — Discover chapters
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=CHAPTER"
|
|
8
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=CHAPTER&projection=line"
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
## Step 3 — Fetch the chapter node (columns + cells)
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$TIMELINE_ID"
|
|
14
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$TIMELINE_ID?projection=cells"
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
Returns `{rows, columns, cells}` directly (not wrapped in `meta.timelineData`).
|
|
18
|
+
|
|
17
19
|
## Step 4a — SCENARIO: append scenarios via the spec endpoint
|
|
18
20
|
|
|
19
21
|
```bash
|
|
@@ -66,7 +68,7 @@ curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=$CELL_ID&timeli
|
|
|
66
68
|
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?colId=$COLUMN_ID&timelineId=$TIMELINE_ID"
|
|
67
69
|
```
|
|
68
70
|
|
|
69
|
-
If you already have fresh
|
|
71
|
+
If you already have a fresh grid loaded from Step 3, reading its `cells` array directly (sparse — an entry only exists once something has been placed there) avoids the extra round-trip; use the calls above otherwise, or when the grid might be stale (e.g. a column was just created).
|
|
70
72
|
|
|
71
73
|
## Step 6 — Insert a column at a specific index (conflict resolution)
|
|
72
74
|
|
|
@@ -176,7 +178,7 @@ curl -s -X POST "http://localhost:3000/api/org/<ORG_ID>/boards/<BOARD_ID>/timeli
|
|
|
176
178
|
|
|
177
179
|
# 2. Fetch chapter to find the target lane cell for the new column
|
|
178
180
|
curl -s -H "x-user-id: place-element-skill" \
|
|
179
|
-
"http://localhost:3000/api/org/<ORG_ID>/boards/<BOARD_ID>/nodes/<TIMELINE_ID
|
|
181
|
+
"http://localhost:3000/api/org/<ORG_ID>/boards/<BOARD_ID>/nodes/<TIMELINE_ID>?projection=cells"
|
|
180
182
|
|
|
181
183
|
# 3. Create the EVENT node — do not skip the x-user-id header
|
|
182
184
|
curl -s -X POST "http://localhost:3000/api/org/<ORG_ID>/boards/<BOARD_ID>/nodes/events" \
|
|
@@ -123,7 +123,7 @@ mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<CHAPTER_ID>"
|
|
|
123
123
|
|
|
124
124
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Step 4 — Fetch the chapter's grid state".
|
|
125
125
|
|
|
126
|
-
Parse the result (`{rows, columns, cells}` directly
|
|
126
|
+
Parse the result (`{rows, columns, cells}` directly — the REST fallback's `?projection=cells` returns the same shape):
|
|
127
127
|
- `rows` — list of row objects, each with `id` and `type`
|
|
128
128
|
- `columns` — list of column objects, each with `id`
|
|
129
129
|
- `cells` — list of cell objects, each with `rowId`, `colId`, and optionally `nodeId` (used only to check occupancy)
|
|
@@ -183,7 +183,7 @@ mcp__eventmodelers__create_screen {
|
|
|
183
183
|
"contentType": "html",
|
|
184
184
|
"nodeId": "<SCREEN_NODE_ID>",
|
|
185
185
|
"chapterId": "<CHAPTER_ID>",
|
|
186
|
-
"
|
|
186
|
+
"cellName": "<actorCellName>",
|
|
187
187
|
"title": "<screenTitle>",
|
|
188
188
|
"pages": ["<div>...</div>"],
|
|
189
189
|
"description": "<screenTitle — what this screen shows>"
|
|
@@ -204,7 +204,7 @@ mcp__eventmodelers__create_screen {
|
|
|
204
204
|
"contentType": "sketch",
|
|
205
205
|
"nodeId": "<SCREEN_NODE_ID>",
|
|
206
206
|
"chapterId": "<CHAPTER_ID>",
|
|
207
|
-
"
|
|
207
|
+
"cellName": "<actorCellName>",
|
|
208
208
|
"title": "<screenTitle>",
|
|
209
209
|
"elements": [...],
|
|
210
210
|
"description": "<screenTitle — what this screen shows>"
|
|
@@ -214,7 +214,7 @@ mcp__eventmodelers__create_screen {
|
|
|
214
214
|
|
|
215
215
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Step 5b — Create the screen node and render it (sketch path, explicit request only)".
|
|
216
216
|
|
|
217
|
-
|
|
217
|
+
MCP `create_screen` takes only `cellName`. Don't derive `actorCellName` from array positions in the grid — read it from `get_board_outline` for that chapter (call it once if you don't already hold it): the column letter is the target column's `letter` there, the row number is the target lane's 1-based position in its `lanes` list (lanes are listed top to bottom, so the first lane is row 1) (e.g. column `letter: "D"`, the actor lane first in `lanes` → `D1`). A column appended by `add_column` only has a letter once you re-read the outline. The curl fallback still takes the computed `actorCellId` as `cellId`. Expect success (MCP: `created: true`; curl: `204`). On failure, read the validation error, fix the payload, and retry once before reporting failure.
|
|
218
218
|
|
|
219
219
|
### Step 5b(ii) — Set field data lineage (mandatory)
|
|
220
220
|
|
|
@@ -15,10 +15,10 @@ Extract `id` from the response → `CHAPTER_ID`.
|
|
|
15
15
|
## Step 4 — Fetch the chapter's grid state
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID"
|
|
18
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID?projection=cells"
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
Returns `{rows, columns, cells}` directly (not wrapped in `meta.timelineData`).
|
|
22
22
|
|
|
23
23
|
## Step 5a — Add a column
|
|
24
24
|
|
|
@@ -31,11 +31,11 @@ From `$ARGUMENTS` and the conversation, extract:
|
|
|
31
31
|
|
|
32
32
|
### 1a — Discover existing timelines
|
|
33
33
|
|
|
34
|
-
Before doing anything else, fetch all chapters (timelines) on the board.
|
|
34
|
+
Before doing anything else, fetch all chapters (timelines) on the board — only ids and titles are needed to list/pick one, so use `projection: "line"` (without it every chapter's full grid comes back).
|
|
35
35
|
|
|
36
36
|
**Prefer MCP:**
|
|
37
37
|
```
|
|
38
|
-
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "CHAPTER" }
|
|
38
|
+
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "CHAPTER", "projection": "line" }
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Fetch all chapters".
|
|
@@ -55,17 +55,17 @@ mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "CHAPTER" }
|
|
|
55
55
|
|
|
56
56
|
A chapter and a timeline are the same thing — the terms are interchangeable.
|
|
57
57
|
|
|
58
|
-
If `timelineId` is provided, first resolve it to a UUID if a name was given instead.
|
|
58
|
+
If `timelineId` is provided, first resolve it to a UUID if a name was given instead (ids and titles only — `projection: "line"`).
|
|
59
59
|
|
|
60
60
|
**Prefer MCP:**
|
|
61
61
|
```
|
|
62
|
-
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "CHAPTER" }
|
|
62
|
+
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "CHAPTER", "projection": "line" }
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Fetch all chapters".
|
|
66
66
|
|
|
67
67
|
- If the value looks like a UUID, use it directly as `CHAPTER_ID`.
|
|
68
|
-
- If it looks like a name, find the CHAPTER node whose `
|
|
68
|
+
- If it looks like a name, find the CHAPTER node whose `title` matches (case-insensitive) and use its `id` as `CHAPTER_ID`.
|
|
69
69
|
- If no match is found, tell the user and stop.
|
|
70
70
|
|
|
71
71
|
Fetch the chapter node to read its grid structure — `projection: "cells"` returns just `{rows, columns, cells}`, not the whole chapter node.
|
|
@@ -77,21 +77,21 @@ mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<CHAPTER_ID>"
|
|
|
77
77
|
|
|
78
78
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Fetch the chapter's grid state".
|
|
79
79
|
|
|
80
|
-
From the result (`{rows, columns, cells}`
|
|
80
|
+
From the result (`{rows, columns, cells}` — both MCP and the REST fallback use `projection=cells`):
|
|
81
81
|
- `rows` — find the row with `type === "swimlane"` and save its `id` as `swimlaneRowId`
|
|
82
82
|
- `columns` — ordered list of columns, each with an `id`
|
|
83
83
|
- `cells` — each cell has `colId`, `rowId`, and optionally `nodeId`
|
|
84
84
|
|
|
85
|
-
Then load the existing EVENT nodes
|
|
85
|
+
Then load the existing EVENT nodes of this chapter — only `id` and `title` are needed (the column comes from `cells`), so use `projection: "line"`.
|
|
86
86
|
|
|
87
87
|
**Prefer MCP:**
|
|
88
88
|
```
|
|
89
|
-
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "EVENT" }
|
|
89
|
+
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "EVENT", "chapterId": "<CHAPTER_ID>", "projection": "line" }
|
|
90
90
|
```
|
|
91
91
|
|
|
92
92
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Fetch all EVENT nodes".
|
|
93
93
|
|
|
94
|
-
For each EVENT node, find its cell in `
|
|
94
|
+
For each EVENT node, find its cell in `cells` where `nodeId === event.id`. That cell's `colId` gives the `columnId`. Order events by their column's position in `columns`.
|
|
95
95
|
|
|
96
96
|
Set `CHAPTER_ID = <resolved uuid>`.
|
|
97
97
|
|
|
@@ -176,7 +176,7 @@ mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<CHAPTER_ID>"
|
|
|
176
176
|
|
|
177
177
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Fetch the chapter's grid state".
|
|
178
178
|
|
|
179
|
-
From the result (`rows`/`columns`/`cells` directly via MCP
|
|
179
|
+
From the result (`rows`/`columns`/`cells` directly, via MCP or the REST fallback's `?projection=cells`):
|
|
180
180
|
- Read `rows` to find and save `swimlaneRowId` (the row whose `type === "swimlane"`).
|
|
181
181
|
- Identify **empty columns**: columns where no cell has a `nodeId` set.
|
|
182
182
|
|
|
@@ -5,21 +5,21 @@ Only needed when MCP is not connected. Every call below has an MCP equivalent in
|
|
|
5
5
|
## Fetch all chapters (Steps 1a, 1b)
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=CHAPTER"
|
|
8
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=CHAPTER&projection=line"
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
## Fetch the chapter's grid state (Steps 1b, 3c)
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID"
|
|
14
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID?projection=cells"
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Returns `{rows, columns, cells}` directly (not wrapped in `meta.timelineData`).
|
|
18
18
|
|
|
19
19
|
## Fetch all EVENT nodes (Step 1b)
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=EVENT"
|
|
22
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=EVENT&chapterId=$CHAPTER_ID&projection=line"
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
## Create a new chapter (Step 1c)
|