@eventmodelers/cli 1.0.122 → 1.0.124

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 CHANGED
@@ -106,6 +106,7 @@ Use skills in Claude Code with `/skill-name`:
106
106
  | `/connect` | Set up board connection |
107
107
  | `/timeline` | Live event storming facilitator |
108
108
  | `/wdyt` | Business analyst review of your event model |
109
+ | `/walkthrough` | Step-by-step player through a process, or through everything a planned change touches |
109
110
  | `/storyboard` | Build a full visual storyboard |
110
111
  | `/html-screen` | Design individual real HTML/CSS screens (default) |
111
112
  | `/storyboard-screen` | Design individual wireframe/sketch screens (explicit request only) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventmodelers/cli",
3
- "version": "1.0.122",
3
+ "version": "1.0.124",
4
4
  "description": "Eventmodelers CLI — real-time Claude agent + skills for Claude Code, for any stack (Node, Supabase, Axon, OpenCQRS, UmaDB, Kurrent, or modeling-only)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -80,11 +80,15 @@ function describeToolUse(block) {
80
80
  // `slice` is set for a planned-slice build (see startRalph) — the only turns that are traced.
81
81
  function runClaude(prompt, slice = null) {
82
82
  return new Promise((resolve, reject) => {
83
- const proc = spawn('claude', [...claudeArgs, '-p', inlineHeader + prompt], {
83
+ // The prompt goes in on stdin, never as a `-p` argument: on Windows a multi-line argv entry
84
+ // is cut at its first newline, so `claude` saw only the connect header, answered "I don't see
85
+ // a request" and the slice stayed Planned until the stuck-slice guard blocked it.
86
+ const proc = spawn('claude', [...claudeArgs, '-p'], {
84
87
  cwd: projectDir,
85
- stdio: ['inherit', 'pipe', 'inherit'],
88
+ stdio: ['pipe', 'pipe', 'inherit'],
86
89
  env: claudeEnv,
87
90
  });
91
+ proc.stdin.end(inlineHeader + prompt);
88
92
  const turn = superviseTurn(proc, { timeoutMs, label: 'Claude turn', log: (line) => console.error(`[ralph] ${line}`) });
89
93
 
90
94
  let buffer = '';
@@ -65,6 +65,13 @@ get_planned_slice_title() {
65
65
  done
66
66
  }
67
67
 
68
+ # prompt.md and backend-prompt.md are instruction manuals with no request in them — handed one
69
+ # alone, the agent may read it as background and ask what to do. Every turn ends with the concrete
70
+ # request, the same one lib/ralph.js's taskRequest() appends.
71
+ task_request() {
72
+ printf '\n\n---\n\nYour task now: %s, following the instructions above. Start immediately.\n' "$1"
73
+ }
74
+
68
75
  # Runs agent.sh with the given prompt; retries on non-zero exit
69
76
  run_agent() {
70
77
  local label="$1"
@@ -82,13 +89,13 @@ while [[ "$ITERATIONS" -eq 0 || "$cycle" -lt "$ITERATIONS" ]]; do
82
89
  ran_something=false
83
90
 
84
91
  if [[ "$HAS_CREDENTIALS" == true ]] && has_pending_tasks; then
85
- run_agent "onTask: loading slice from board..." "$(cat "$PROMPT_FILE")"
92
+ run_agent "onTask: loading slice from board..." "$(cat "$PROMPT_FILE"; task_request 'process the pending tasks in tasks.json')"
86
93
  ran_something=true
87
94
  fi
88
95
 
89
96
  if has_planned_slices; then
90
97
  slice_title=$(get_planned_slice_title)
91
- run_agent "onPlannedSlice: building \"$slice_title\"..." "$(cat "$BACKEND_PROMPT_FILE")"
98
+ run_agent "onPlannedSlice: building \"$slice_title\"..." "$(cat "$BACKEND_PROMPT_FILE"; task_request "build the Planned slice \"$slice_title\"")"
92
99
  echo "[$(date -u +%H:%M:%S)] Slice \"$slice_title\" build complete — waiting for next slice"
93
100
  ran_something=true
94
101
  fi
@@ -30,7 +30,7 @@ Write tools take their items as an array (`nodeIds[]`, `elements[]`, `connection
30
30
  | `get_node` | `boardId`, `nodeId`, `projection?` (`"cells"` \| `"edges"`) | Get one node (no `cellName` — address it by `nodeId`; cell addresses come only from `get_board_outline`). `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. An unknown id is a `NODE_NOT_FOUND` error | §3 `GET .../nodes/:nodeId` (REST returns the node as stored, without `cellName`; `404` carries `code: 'NODE_NOT_FOUND'`) |
31
31
  | `get_node_comments` | `boardId`, `nodeId` | List comments on a node | §1 `GET .../nodes/:nodeId/comments` |
32
32
  | `get_chat_attachment` | `boardId`, `sessionId`, `attachmentId` | The file a chat message came with — the message's `attachments` list `{id, type, ending}`. Images come back as images, a PDF as an embedded resource, csv / xml / txt / json as text. Only the agent the message was addressed to can read it (`CHAT_ATTACHMENT_NOT_FOUND`); needs the connection's `x-agent-id` (`CHAT_AGENT_REQUIRED`). Max 5 MB per file. | §15 `GET /api/org/:orgId/chat/sessions/:sessionId/attachments/:attachmentId` |
33
- | `post_chat_message` | `boardId`, `text`, `replyTo?`, `sessionId?`, `preferUserId?`, `snippet?` | Write into a person's chat — a reply (`replyTo` marks their message answered) or a question you start (`preferUserId`). `text` ≤ 1000 characters. `snippet` attaches one interactive element under the text (confirm, poll, tasks, changes, link, image, code, report); the person's choice comes back as their next chat message. See §16 | §16 `POST /api/org/:orgId/boards/:boardId/chat/agent-messages` |
33
+ | `post_chat_message` | `boardId`, `text`, `replyTo?`, `sessionId?`, `preferUserId?`, `snippet?` | Write into a person's chat — a reply (`replyTo` marks their message answered) or a question you start (`preferUserId`). `text` ≤ 1000 characters. `snippet` attaches one interactive element under the text (confirm, poll, tasks, changes, link, image, code, report, walkthrough); the person's choice comes back as their next chat message. See §16 | §16 `POST /api/org/:orgId/boards/:boardId/chat/agent-messages` |
34
34
  | `get_board_events` | `boardId`, `since?`, `seq?` | A page of at most 300 board events, oldest first, as `{events, pageSize, hasMore}`. Not the whole log: while `hasMore` is true, page on by passing the last event's `seq` as `since`. `seq` returns only the event of exactly that seq | §1 `GET .../events` |
35
35
  | `submit_node_events` | `boardId`, `events[]`, `autoConnect?`, `compact?` | Create/update nodes (raw `NodeChangeEvent`/edge events). Every event property is described on the tool's own `events[]` schema — read that rather than this skill when all you need is the event shape. An event needs its own `id` (required — a fresh uuid per event, never the node id) and its `eventType`, plus the keys of its kind (`nodeId`/`meta`/`node`, or `edgeId`/`source`/`target`). The `id` keys the returned `hashes` map and makes a resubmit idempotent — the same event sent twice under one id writes a single board event. Don't send `boardId` or `timestamp` per event — the server takes the board from the call and stamps the time itself, and ignores both if sent. `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` |
36
36
  | `delete_node` | `boardId`, `nodeIds[]` | Delete nodes, applied in order — one failure doesn't stop the rest; answers `{results}`. 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) |
@@ -1120,8 +1120,9 @@ Rule of thumb: if you are about to write "Shall I…?", "Want me to…?", "A or
1120
1120
  | to show a picture | `image` | nothing |
1121
1121
  | to show a command or payload | `code` | nothing |
1122
1122
  | to show numbers as a diagram (counts per slice, a trend, shares of a whole) | `report` | nothing |
1123
+ | to explain how a process works, or walk through what a planned change touches, step by step ("how does registration work?", "we need to add an email — walk me through it") | `walkthrough` | nothing — selecting a step pans the canvas |
1123
1124
 
1124
- `confirm`, `poll`, `tasks` and `changes` are questions: a person answers once, then they show as answered. `link`, `image`, `code` and `report` are display only and never wait for anything.
1125
+ `confirm`, `poll`, `tasks` and `changes` are questions: a person answers once, then they show as answered. `link`, `image`, `code`, `report` and `walkthrough` are display only and never wait for anything.
1125
1126
 
1126
1127
  ### Shapes
1127
1128
  ```typescript
@@ -1143,6 +1144,13 @@ Rule of thumb: if you are about to write "Shall I…?", "Want me to…?", "A or
1143
1144
  labels: string[], // 1–50 points: x axis, or the pie slices
1144
1145
  series: Array<{ name: string, values: number[] }>, // one number per label; bar/line up to 6 series, pie uses only the first (no negatives)
1145
1146
  confirm?: { headline: string, yesLabel?: string, noLabel?: string } } // optional yes/no question under the diagram, like a confirm snippet
1147
+ { kind: 'walkthrough', title: string, intro?: string, // a step-by-step player through a process
1148
+ steps: Array<{ title: string, // 1+, in the order the process runs
1149
+ description?: string, // what happens in this step, in business words
1150
+ nodeId?: string, // the element, slice (SLICE_BORDER) or scenario (SCENARIO) of this step — selecting the step zooms to it
1151
+ screenId?: string, // the SCREEN / HTML_SCREEN the person sees in this step
1152
+ rules?: string[], // business rules that apply in this step
1153
+ changes?: string[] }> } // for a planned change: what has to change here
1146
1154
  ```
1147
1155
 
1148
1156
  ### How to use them
@@ -1153,6 +1161,7 @@ Rule of thumb: if you are about to write "Shall I…?", "Want me to…?", "A or
1153
1161
  - **Answers are text.** For a `tasks` answer, match the titles (or ids) back to your list; an element the person left unticked was declined, not forgotten. A `poll` option with a `message` of your own makes the answer unambiguous.
1154
1162
  - `get_chat_session` shows your snippet on the message as `snippet`, and the person's answer is the user message after it, carrying `context.snippetReply.messageId` (your message's id) — useful to tell an answer to a snippet from a new message.
1155
1163
  - **`report`** draws the numbers you send — it computes nothing, so aggregate first. A series whose length differs from `labels`, or a non-number, makes the whole snippet invisible. Keep `text` as a one-line summary of what the diagram shows. Add `confirm` when the diagram leads to a follow-up question ("Want me to look into the backward arrows?"): the answer comes back like a `confirm` answer, and without it the report is display only.
1164
+ - **`walkthrough`** answers "how does X work?" and "walk me through what changes for Y": the person steps back and forth through the process in the chat, each step zooming the canvas to its element, and **Play** opens it full-screen as a movie strip of the steps' screens with description and rules underneath. Build it from the board, never from memory — the `walkthrough` skill (modeling kit) has the procedure. Every `nodeId`/`screenId` must be a real id from this board; a step without a screen just shows its element. Keep `text` to one line naming the process and the number of steps.
1156
1165
  - A snippet is shown exactly as you send it — keep secrets and tokens out of `code` and `image`.
1157
1166
 
1158
1167
  ---
@@ -26,11 +26,13 @@ const claudeEnv = cfg.anthropicBaseUrl
26
26
 
27
27
  function runClaude(prompt) {
28
28
  return new Promise((resolve, reject) => {
29
- const proc = spawn('claude', [...claudeArgs, '-p', inlineHeader + prompt], {
29
+ // Prompt on stdin, not as a `-p` argument — Windows cuts a multi-line argv entry at its first newline.
30
+ const proc = spawn('claude', [...claudeArgs, '-p'], {
30
31
  cwd: projectDir,
31
- stdio: 'inherit',
32
+ stdio: ['pipe', 'inherit', 'inherit'],
32
33
  env: claudeEnv,
33
34
  });
35
+ proc.stdin.end(inlineHeader + prompt);
34
36
  proc.on('close', (code) => (code === 0 ? resolve() : reject(new Error(`Claude exited ${code}`))));
35
37
  proc.on('error', reject);
36
38
  });
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: detect-model-drift
3
- description: Compare the event model on the board with the code in this repository and report where they have drifted apart — fields on commands/events/read models, specifications vs tests, screens, edges, slices missing in code, and slices in code that the model doesn't have. Read-only; every finding comes with a suggested fix the user confirms first.
3
+ description: Compare the event model on the board with the code in this repository and report where they have drifted apart — fields on commands/events/read models, specifications vs tests, screens, edges, slices missing in code, and slices in code that the model doesn't have. `--branch <name>` compares against that branch's code (default: the current branch's working tree). Read-only; every finding comes with a suggested fix the user confirms first.
4
4
  ---
5
5
 
6
6
  # Detect Model Drift
@@ -15,6 +15,8 @@ If it is not (an empty folder, a modeling-only workspace holding just `.claude/`
15
15
 
16
16
  Do not connect, do not read the board, do not produce a report or snippet, and do not offer other fixes.
17
17
 
18
+ **With `--branch <name>`** (see Step 1), also check that the branch exists in that code base: `git rev-parse --verify <name>`, else `git fetch origin <name>` and `git rev-parse --verify origin/<name>`. Use whichever resolves as `REF`. If neither does, stop the same way in one sentence (*"There is no branch `<name>` in this repository."*).
19
+
18
20
  ---
19
21
 
20
22
  > **Once Step 0 passes**, invoke the `connect` skill — if not already connected — to resolve `TOKEN`, `BOARD_ID`, `ORG_ID`, and `BASE_URL`. Do not proceed until the connect skill has completed.
@@ -34,6 +36,7 @@ From `$ARGUMENTS` and the prompt context, extract:
34
36
  | `timelineId` | `context.timelineId` of the prompt (overrules the prompt's own `timeline_id`), or a timeline/chapter named in the text | whole board |
35
37
  | `contextName` | a bounded context named in the text | all contexts |
36
38
  | `sliceTitle` | a single slice named in the text | all slices in scope |
39
+ | `branch` | `--branch <name>` in `$ARGUMENTS`, or a branch named in the text | the current branch's working tree |
37
40
 
38
41
  **If a timeline is in focus, only that timeline's slices are compared** — both on the board side and, through the slice → code mapping of Step 3, on the code side. Code that belongs to no slice in scope is not reported as "additional" in a scoped run; say so in the report footer instead of guessing.
39
42
 
@@ -62,6 +65,8 @@ Index the result in memory: per slice → commands, events, read models, screens
62
65
 
63
66
  The code base is the directory resolved in Step 0 (`$EVENTMODELERS_CODE_DIR`, else the current directory) — every path below is relative to it, and it may hold any stack. Find out which, don't assume.
64
67
 
68
+ **With a `branch` other than the one checked out, read the code from git, never from the working tree:** `git ls-tree -r --name-only $REF [<dir>]` lists the files, `git show $REF:<path>` reads one. That applies to everything below — `.build-kit/CLAUDE.md`, the `.build-kit/.slices` snapshot, the slice folders. Never `git checkout`/`git switch`: the working tree may hold someone's uncommitted work. Without `branch`, or when it is the current branch, read the working tree as usual.
69
+
65
70
  1. Read `.build-kit/CLAUDE.md` (and `.build-kit/AGENTS.md` if present) — its *Structure* section says where slices live and how they're named (e.g. `src/slices/{slice}/` for Node, `src/main/java/.../slices/{context}/{slice}/` for Axon).
66
71
  2. If `.build-kit/.slices/<context>/<slice>/slice.json` exists, it is the snapshot the code was **last built from** — useful for telling "the code changed" apart from "the model changed since the build" (see Step 5).
67
72
  3. Without a build kit, infer the layout from the repo (folders named after slices, `*Command*`, `*Event*`, projection/read-model files, route/UI files). If you cannot find any slice structure, stop and tell the user what you looked at — do not fabricate a mapping.
@@ -104,7 +109,7 @@ For every finding, pick who should move:
104
109
 
105
110
  | Evidence | Fix goes to | How it's done |
106
111
  |----------|-------------|---------------|
107
- | Code differs from `.build-kit/.slices` snapshot, board still equals the snapshot | **Model** — the code moved on deliberately | Skill that changes the board: `/attributes` (fields), `/timeline` / `/place-element` (elements), `/eventmodeling-elaborating-scenarios` (specs), `/storyboard-screen` or `/html-screen` (screens — including updating every existing screen connected to a changed element, see below) |
112
+ | Code differs from `.build-kit/.slices` snapshot (read from the same branch as the code), board still equals the snapshot | **Model** — the code moved on deliberately | Skill that changes the board: `/attributes` (fields), `/timeline` / `/place-element` (elements), `/eventmodeling-elaborating-scenarios` (specs), `/storyboard-screen` or `/html-screen` (screens — including updating every existing screen connected to a changed element, see below) |
108
113
  | Board differs from the snapshot, code still equals it | **Code** — the model moved on | Set the slice back to `Planned` with `/update-slice-status` so the build kit picks it up again and diffs code against `slice.json` |
109
114
  | Both moved, or no snapshot | **Judgement call** — present both options, recommend one | Recommend by which side is more specific/complete |
110
115
  | Slice only in code | **Model** | Add the slice, via `/eventmodeling-slicing-event-models` after placing its elements |
@@ -155,7 +160,7 @@ Name each slice and element as `[<title>](ref:<nodeId>)` (the slice border's id
155
160
 
156
161
  ```
157
162
  ## Model Drift — <scope: board | timeline "<title>" | context "<name>">
158
- Compared: <n> slices on the board, <n> slice folders in code Analysed: <ISO timestamp>
163
+ Compared: <n> slices on the board, <n> slice folders in code<, branch <name> @ <short sha>> Analysed: <ISO timestamp>
159
164
 
160
165
  ### Slices
161
166
  - [ ] <slice> — in code, not on the board → add it to the model
@@ -183,4 +188,5 @@ Omit empty sections. Say plainly when nothing drifted.
183
188
  - A scoped run (timeline in focus) never reports "slice only in code" for code outside the scope.
184
189
  - One difference, one finding: follow a rename along the chain instead of listing it per element.
185
190
  - Fixing drift on the model side means updating the existing screens connected to the changed elements too (pages and `meta.fields`), in place — not just the command/event/read model.
191
+ - `--branch` reads the code via `git ls-tree`/`git show` from that ref — never check it out, the working tree may hold someone's uncommitted work. No `--branch` = the working tree, as before.
186
192
  - Always read the MARKDOWN note in the feedback lane of each chapter's first column first — it is the chapter's context, and differences it explains are not drift.
@@ -0,0 +1,146 @@
1
+ ---
2
+ name: walkthrough
3
+ description: Walk a person through the board step by step, as a player in the chat. Two modes — explain ("how does registration work?") follows a process through its screens, commands, events and read models; impact ("we need to add an email — walk me through the changes") lists, in order, every slice, element and scenario a planned change touches and what has to change there. Each step carries a description, the business rules that apply, a link that zooms the canvas to it, and its screen; the person can play it full-screen like the screen movie strip. Read-only — changes nothing on the board.
4
+ ---
5
+
6
+ # Walkthrough — step by step through the board
7
+
8
+ > **Before doing anything else**, invoke the `connect` skill — if not already connected — to resolve `TOKEN`, `BOARD_ID`, `ORG_ID`, and `BASE_URL`. Do not proceed until it has completed.
9
+
10
+ Prefer `mcp__eventmodelers__*` tools. See `learn-eventmodelers-api` for the curl fallback and §16 *Chat snippets* for the `walkthrough` shape.
11
+
12
+ The result is **one `walkthrough` snippet** in the chat. The person steps back and forth through it; selecting a step zooms the canvas to that step's element, slice or scenario, and **Play** opens all steps full-screen as a strip of their screens with the description, the changes and the rules underneath. The walkthrough is only as good as its links: every step points at a real node on this board.
13
+
14
+ **This skill never writes to the board.** In impact mode it shows what would change; making those changes is separate work the person asks for afterwards.
15
+
16
+ ---
17
+
18
+ ## Step 1 — Mode and subject
19
+
20
+ From `$ARGUMENTS` / the message, and the turn's `context=` (selected nodes, `timelineId`, `focusArea` — resolved as in the kit's CLAUDE.md):
21
+
22
+ | Mode | Typical message | The steps are |
23
+ |---|---|---|
24
+ | `explain` | "How does registration work?", "walk me through checkout", "/walkthrough Registration" | the process in the order it runs, as a person experiences it |
25
+ | `impact` | "We need to add an email — walk me through the changes", "what does it take to support refunds?" | every place in the model the change touches, in the order it should be changed |
26
+
27
+ The **subject** is the process or the change, in the person's words. "This"/"here" means what they have selected or zoomed to.
28
+
29
+ ### No subject yet — ask first
30
+
31
+ Called without one (a bare `/walkthrough`, the chat menu's entry sent as is, "walk me through" with nothing named), **ask before reading anything in detail** — the selection or the chapter in view is not enough on its own to guess which flow or which feature they mean. One cheap read for the options: `get_nodes { boardId, type: "CHAPTER", projection: "line" }` (and, for a single chapter, `list_slices`). Then reply once and **end the turn**:
32
+
33
+ ```
34
+ mcp__eventmodelers__post_chat_message {
35
+ "boardId": "$BOARD_ID",
36
+ "replyTo": "<message_id>",
37
+ "text": "Which flow should I walk you through? Or tell me the new feature you're planning, and I'll walk you through what it changes.",
38
+ "snippet": {
39
+ "kind": "poll",
40
+ "question": "Walk through which flow?",
41
+ "options": [
42
+ { "label": "Registration", "message": "/walkthrough How does Registration work?" },
43
+ { "label": "Checkout", "message": "/walkthrough How does Checkout work?" }
44
+ ]
45
+ }
46
+ }
47
+ ```
48
+
49
+ - The options are the board's flows — chapters, or the main slices when the board has one chapter — at most ~8, the one in the person's view first. Each option's `message` is a complete `/walkthrough …` request, so the pick comes back as a new message that runs this skill with its subject.
50
+ - The text always offers the **impact** mode in words ("tell me the new feature you're planning") — a planned change can't be picked from a list; the person types it, and their reply is the subject.
51
+ - Fewer than two flows on the board: ask in the text only, no snippet. An empty board: say there is nothing to walk through yet.
52
+
53
+ The same goes for a subject that is still too vague with the context (several chapters equally likely): ask with a `poll` of those candidates — don't guess between them.
54
+
55
+ ---
56
+
57
+ ## Step 2 — Outline first: where on the board is it?
58
+
59
+ Orient cheaply before reading anything in detail — the board can be large, and only a handful of nodes matter.
60
+
61
+ 1. **Chapters**: `get_nodes { boardId, type: "CHAPTER", projection: "line" }` — pick the chapter(s) the subject belongs to by title. With a `timelineId` in context, start there.
62
+ 2. **Context notes**: for each candidate chapter, `get_board_outline { boardId, chapterId }`, then read the MARKDOWN note in the first column's `feedback` lane (`get_node`, `meta.description`) if there is one. It says what the chapter is for and often names the decisions behind it — use it in the descriptions.
63
+ 3. **Slices**: `list_slices { boardId, contextName }` and `get_slice_data { boardId, contextName, projection: "outline", format: "toon" }` — every slice with its elements as `{id, title, type}`.
64
+ 4. **Find the nodes that matter.** With the outline in hand, decide which slices and elements belong to the subject:
65
+ - **explain**: the slices whose commands, events, read models or screens are about the subject; follow the edges in the outline (screen → command → event → read model → next screen, events → automations) from where the process starts to where it ends. Other chapters only when the flow crosses into them (an external or translated event).
66
+ - **impact**: start from the element the change lands on first (the screen or command where the new data enters, or the event whose meaning changes), then follow the flow **downstream** — every event that would carry it, every read model and automation fed by those events, every screen that shows those read models — and **upstream** where the data has to come from. A name search helps: `get_nodes { boardId, name: "<term>", projection: "line" }` for the field or concept ("email") finds the places that mention it already.
67
+
68
+ Stop widening when a further step adds nothing the person asked about. A walkthrough of 4–12 steps is typical; 20+ means the subject is really several — say so in the text and walk the first one.
69
+
70
+ ---
71
+
72
+ ## Step 3 — Read only those
73
+
74
+ Now read the working set in detail — never the whole board:
75
+
76
+ - `get_slice_data { boardId, contextName, sliceId, projection: "fields" }` per relevant slice — fields and dependencies.
77
+ - `get_slice_data { boardId, contextName, sliceId, projection: "specs", format: "toon" }` — the **scenarios**: these are the business rules. A scenario's title in business words ("Email must be unique") is a rule; its Given/When/Then says when it applies.
78
+ - `get_nodes { boardId, type: "SCENARIO", chapterId, projection: "line" }` — the ids of the scenario nodes, so an impact step can point at a scenario directly.
79
+ - Full `get_nodes { boardId, nodeIds: [...] }` only for the few nodes whose `description` you need.
80
+
81
+ ---
82
+
83
+ ## Step 4 — Build the steps
84
+
85
+ One step per thing the person should look at, in order.
86
+
87
+ **explain** — follow the process as the person living it experiences it. One step per meaningful moment, usually one slice: *what they see → what they do → what is recorded → what that makes visible or triggers*. Merge a command and its event into one step when nothing happens between them; give an automation its own step.
88
+
89
+ **impact** — one step per place that has to change, ordered so each step builds on the previous one (where the data enters → the command → the event → read models → screens → automations → scenarios). Include **scenarios** as their own steps when they need a new case or an update (e.g. "add: Register with an email that is already taken → rejected"). Say what is *not* affected only if the person would reasonably expect it to be.
90
+
91
+ Per step:
92
+
93
+ | Field | What goes in |
94
+ |---|---|
95
+ | `title` | short, business words — the moment ("Customer submits registration") or the place ("Register Customer — command") |
96
+ | `description` | 1–3 sentences: what happens here and why it matters, in the board's language. No ids, no cell addresses. |
97
+ | `nodeId` | the node to zoom to: the **slice** (its `SLICE_BORDER` id from `list_slices`) when the step is about the slice as a whole, the **element** when it is about one element, the **scenario** node when the step is a scenario |
98
+ | `screenId` | the SCREEN / HTML_SCREEN shown in this step, if there is one — that is what the player shows. Leave it out otherwise; don't point at an unrelated screen to fill the frame |
99
+ | `rules` | the business rules that apply here, taken from the scenarios (and the context note); one line each, thresholds verbatim. Empty when there are none — never invent one |
100
+ | `changes` | **impact mode only**: what has to change here, one line each — "add field `email` (String)", "new scenario: duplicate email is rejected", "show email in the confirmation screen" |
101
+
102
+ **Faithfulness.** Everything comes from the board. A rule nobody modelled is not a rule — if the walkthrough reveals a gap ("nothing says what happens when confirmation never arrives"), mention it in the step's `description` as an open question, not as a rule.
103
+
104
+ ---
105
+
106
+ ## Step 5 — Post it
107
+
108
+ **One** `post_chat_message`: in a `CHAT` turn `replyTo` = this turn's `message_id`; in a prompt turn that came from a chat, `sessionId` = `CHAT_SESSION_ID` and no `replyTo`.
109
+
110
+ ```
111
+ mcp__eventmodelers__post_chat_message {
112
+ "boardId": "$BOARD_ID",
113
+ "replyTo": "<message_id>",
114
+ "text": "Registration in 5 steps — pick a step to jump to it, or press Play.",
115
+ "snippet": {
116
+ "kind": "walkthrough",
117
+ "title": "How registration works",
118
+ "intro": "From the sign-up form to a confirmed account.",
119
+ "steps": [
120
+ { "title": "Customer fills in the sign-up form", "nodeId": "<slice id>", "screenId": "<screen id>",
121
+ "description": "Name, email and password are entered on the registration screen.",
122
+ "rules": ["Email must be unique", "Password at least 12 characters"] },
123
+ { "title": "Customer registered", "nodeId": "<event id>",
124
+ "description": "The registration is recorded; the account is not active yet." }
125
+ ]
126
+ }
127
+ }
128
+ ```
129
+
130
+ Impact mode, same shape with `changes` per step:
131
+
132
+ ```
133
+ { "title": "Register Customer — command", "nodeId": "<command id>",
134
+ "description": "The email enters here and has to be validated before anything is recorded.",
135
+ "changes": ["Add field email (String)", "Reject the command when the email is already registered"],
136
+ "rules": ["Customer name is required"] },
137
+ { "title": "Scenario: duplicate email", "nodeId": "<scenario id>",
138
+ "changes": ["New case: Given Customer registered with x@y.z, When Register Customer with x@y.z, Then rejected"] }
139
+ ```
140
+
141
+ - `text` is one line: the subject and the number of steps. It is the fallback, so it must make sense alone.
142
+ - Every `nodeId` / `screenId` is an id you read in Steps 2–3 — never a guessed or remembered one.
143
+ - The snippet is at most 64 KB; keep descriptions short rather than dropping steps.
144
+ - **Impact mode** ends there: the walkthrough is the proposal. If the person then asks for the changes, that is ordinary work (prompts per the kit's CLAUDE.md) — and a slice that is not `Created` still needs their confirmation first.
145
+
146
+ **No chat** (run from a terminal): print the steps as a numbered list with the same fields instead of posting.
@@ -202,6 +202,12 @@ for it. Instead:
202
202
  (`timelineId` for the chapter, what they have selected). It sends its findings back as a snippet to tick
203
203
  and posts nothing to the board until they answer (`/wdyt` Step 4.0). Your reply here is one line —
204
204
  *"On it — looking at Registration."*
205
+ - **walkthrough** — "how does X work?", "walk me through …", "what does it take to add …?", or `/walkthrough …`:
206
+ run the `/walkthrough` skill **in this turn** — it only reads the board, so no prompt is needed. It finds the
207
+ slices, elements and scenarios involved and its `walkthrough` snippet is your one reply (step 4). Without a
208
+ subject (a bare `/walkthrough`) it asks first — which flow, or which new feature — and that question is the reply. A planned change
209
+ ("we need to add an email") is its impact mode: the walkthrough is the proposal, and the changes become work only
210
+ when the person asks for them afterwards.
205
211
  - **a skill by name** — the message starts with `/<name>` of one of your skills (the chat panel's menu
206
212
  writes these: `/analyze-existing-model`, `/analyze-code-base`, `/detect-model-drift`, `/add-next-slice`, `/examples`, `/timeline …`,
207
213
  `/html-screen …`, `/storyboard …`): the person chose that skill, so it **is** the work — create one prompt,
@@ -358,6 +364,7 @@ nothing to sanitize either, since a board change is not user text.
358
364
  | Analyse the existing model structure, slice coverage, element counts | `/analyze-existing-model` |
359
365
  | Reverse-engineer the business process of an existing code base into the model, as an interview — all chapters first, then one chapter at a time | `/analyze-code-base` |
360
366
  | Find where the model and the code have drifted apart (fields, specs, screens, edges, missing/extra slices) and propose fixes | `/detect-model-drift` |
367
+ | Walk someone through a process step by step, or through every slice, element and scenario a planned change touches, as a player in the chat | `/walkthrough` |
361
368
  | Turn a document or image (e.g. a tender/RFP) into a chaptered event model with screens, scenarios and a written blueprint summary | `/summarize` |
362
369
  | Look up any API endpoint or element type not already covered by the skill you're executing | `/learn-eventmodelers-api` |
363
370
  | Add or rename an attribute across a chain of elements | `/attributes` |