@eventmodelers/cli 2.0.46 → 2.0.47

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventmodelers/cli",
3
- "version": "2.0.46",
3
+ "version": "2.0.47",
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": {
@@ -35,7 +35,7 @@ Write tools take their items as an array (`nodeIds[]`, `elements[]`, `connection
35
35
  | `place_element` | `boardId`, `timelineId`, `elements[]` (each `action?` = `create` (default) with `elementType`, `title`, `fields?`, `lane?`, `columnIndex?`, `autoConnect?` · `move` with `nodeId` + target `cellName` · `clear` with `nodeId` (preferred for an existing node) or `cellName`), `compact?` | Find/create an empty cell in the right lane and place a COMMAND/READMODEL/EVENT/AUTOMATION/MARKDOWN/TABLE. `action: "move"` puts an existing node of any type into that (empty) cell, moving it if it already sits elsewhere on the timeline; `action: "clear"` unplaces a node without deleting it (`delete_node` deletes) — pass its `nodeId` to empty the cell it sits in, or a `cellName`. These replace `drop_node_to_cell`, `move_node_in_timeline` and `clear_cell`, which still exist but are deprecated — don't use them. `fields` writes the element's attributes in the same call — don't follow a placement with a `submit_node_events` just to set them. A whole slice's or column run's worth goes in one call, applied in order so each entry sees the columns the previous one added; answers `{results}` (`compact: true` → `{nodeId, cellName, columnIndex}` per entry). `autoConnect: false` places without wiring to timeline neighbors — wire the edges yourself | — (MCP-only convenience; composes §2+§3) |
36
36
  | `list_slices` | `boardId`, `contextName?` | List slices (id, title, status). `contextName` restricts to one MODEL_CONTEXT (or timeline) — an unknown name is a `CONTEXT_NOT_FOUND` error, not an empty list | §8 `GET .../slicedata/slices` |
37
37
  | `get_slice_rework` | `boardId`, `contextId` | How much each slice of one context has been reworked: changes, steps backwards, and reopens after Done, most reworked first, plus planning metrics over them. `contextId` is a MODEL_CONTEXT or a timeline (a timeline resolves to the context it belongs to). Always per context — there is no board-wide form | §8 `GET .../reporting/rework/contexts/:contextId` |
38
- | `update_slice_status` | `boardId`, `newStatus`, plus exactly one of `sliceId` / `sliceTitle` | Change a SLICE_BORDER's `sliceStatus`. With a title there is no need to call `list_slices` first; an ambiguous title comes back with its candidates. A slice being created takes its status from `create_slice`/`create_slice_definition` instead | — (via `node:changed` event, §3) |
38
+ | `update_slice_status` | `boardId`, `newStatus`, `statusReason?` (optional, but you should set it: one sentence — why; shown as an info icon in the planning view; stored as `meta.statusReason` next to `sliceStatus`), plus exactly one of `sliceId` / `sliceTitle` | Change a SLICE_BORDER's `sliceStatus`. With a title there is no need to call `list_slices` first; an ambiguous title comes back with its candidates. A slice being created takes its status from `create_slice`/`create_slice_definition` instead | — (via `node:changed` event, §3) |
39
39
  | `get_slice_data` | `boardId`, `contextName?`, `contextId?`, `sliceId?`, `projection?` (`"outline"` \| `"fields"` \| `"specs"`), `format?` | Full element graph for slices in a context. `projection` trims it first: `outline` = slice header + each element as `{id, title, type}`; `fields` = elements with fields and dependencies, no specs/comments/prompts; `specs` = specifications and storylines, no elements — prefer one over the full graph when only part is needed. `outline`/`specs` encode only as `json`, `yaml` or `toon` (other formats are a `PROJECTION_FORMAT_UNSUPPORTED` error); an unknown `sliceId` is `SLICE_NOT_FOUND` | §8 `GET /slicedata` |
40
40
  | `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` |
41
41
  | `get_board_outline` | `boardId`, `chapterId` | One chapter's structure, compact: per-column node lists (`{id, type, title, lane, cellName}`) + a flat edge list, no HTML pages / field bodies / meta. `cellName` is each node's real cell address (column letter + row number) — the only read that returns cell names at all; use it when a placement needs a cell address. For an empty target cell, take the column's `letter` plus the lane's 1-based position in the outline's `lanes` list (e.g. `letter: "C"`, second lane → `C2`) — never convert array indexes yourself. The cheap "what is where and how is it wired" read — prefer over `get_nodes` (no projection) for orientation checks | — (MCP-only convenience) |
@@ -129,10 +129,12 @@ or contradictory — not just "please clarify this slice."
129
129
 
130
130
  ## Step 4 — Mark the slice Blocked
131
131
 
132
+ Blocking is two things: the comment from Step 3 holds the full question, and the `statusReason` is its one-sentence summary (what is missing or contradictory) that people see as an exclamation-mark icon in the planning view and on the slice header. Set both — the reason is optional, but you should always give it here.
133
+
132
134
  Prefer MCP:
133
135
 
134
136
  ```
135
- mcp__eventmodelers__update_slice_status { "boardId": "<BOARD_ID>", "sliceId": "<SLICE_NODE_ID>", "newStatus": "Blocked" }
137
+ mcp__eventmodelers__update_slice_status { "boardId": "<BOARD_ID>", "sliceId": "<SLICE_NODE_ID>", "newStatus": "Blocked", "statusReason": "<one sentence: what is missing>" }
136
138
  ```
137
139
 
138
140
  Also mark the slice 'blocked' locally in the index.json if possible.
@@ -149,10 +151,11 @@ curl -s -X POST "<BASE_URL>/api/org/<ORG_ID>/boards/<BOARD_ID>/nodes/events" \
149
151
  "id": "<new-random-uuid>",
150
152
  "eventType": "node:changed",
151
153
  "nodeId": "<SLICE_NODE_ID>",
152
- "changedAttributes": ["sliceStatus"],
154
+ "changedAttributes": ["sliceStatus", "statusReason"],
153
155
  "meta": {
154
156
  "type": "SLICE_BORDER",
155
- "sliceStatus": "Blocked"
157
+ "sliceStatus": "Blocked",
158
+ "statusReason": "<one sentence: what is missing>"
156
159
  }
157
160
  }]'
158
161
  ```
@@ -17,6 +17,7 @@ From `$ARGUMENTS`, extract:
17
17
  |-------|---------------|---------|
18
18
  | `sliceName` | the slice title to update (case-insensitive match) | **required** |
19
19
  | `newStatus` | the target status value | **required** |
20
+ | `statusReason` | ONE sentence saying why the status changes | optional, but you **should** set it |
20
21
 
21
22
  Valid status values (case-sensitive):
22
23
 
@@ -35,6 +36,13 @@ Valid status values (case-sensitive):
35
36
 
36
37
  If `newStatus` is not one of these exact values, stop and tell the user the valid options.
37
38
 
39
+ ### Give a reason (optional, but you should)
40
+
41
+ `statusReason` is optional — a status change works without it — but whenever you change a status you **should** send one: **one sentence**, in plain words, saying *why* the status changes
42
+ ("Backend built and tested, UI contract published.", "Blocked: the order total rule is unclear.", "Reviewed and approved by Anna.").
43
+ It is stored next to `sliceStatus` in the SLICE_BORDER's meta and shown to people as an exclamation-mark icon in the planning view and on the slice header,
44
+ so write it for a person who was not watching. Say what happened, not the status name again. For `Blocked`, also post a comment with the details (see `request-feedback`) and let `statusReason` be its short summary. Don't leave an old reason in place when the status changes — send a new one each time.
45
+
38
46
  ---
39
47
 
40
48
  ## Step 2 — List all slices
@@ -69,13 +77,13 @@ Save the matched slice as:
69
77
  Prefer the MCP tool — it does the same `node:changed`/`sliceStatus` update in one call, no event envelope to hand-assemble:
70
78
 
71
79
  ```
72
- mcp__eventmodelers__update_slice_status { "boardId": "<BOARD_ID>", "sliceId": "<SLICE_NODE_ID>", "newStatus": "<newStatus>" }
80
+ mcp__eventmodelers__update_slice_status { "boardId": "<BOARD_ID>", "sliceId": "<SLICE_NODE_ID>", "newStatus": "<newStatus>", "statusReason": "<one sentence: why>" }
73
81
  ```
74
82
 
75
83
  The tool also resolves the slice itself — pass `sliceTitle` (or `columnId`) instead of `sliceId` and the listing step above can be skipped entirely; an ambiguous title comes back with its candidates instead of a guess:
76
84
 
77
85
  ```
78
- mcp__eventmodelers__update_slice_status { "boardId": "<BOARD_ID>", "sliceTitle": "Place Order", "newStatus": "<newStatus>" }
86
+ mcp__eventmodelers__update_slice_status { "boardId": "<BOARD_ID>", "sliceTitle": "Place Order", "newStatus": "<newStatus>", "statusReason": "<one sentence: why>" }
79
87
  ```
80
88
 
81
89
  A slice that is being *created* takes its status straight from `create_slice`/`create_slice_definition` (`status`) — don't create it and then call this.
@@ -91,10 +99,11 @@ curl -s -X POST "<BASE_URL>/api/org/<ORG_ID>/boards/<BOARD_ID>/nodes/events" \
91
99
  "id": "<new-random-uuid>",
92
100
  "eventType": "node:changed",
93
101
  "nodeId": "<SLICE_NODE_ID>",
94
- "changedAttributes": ["sliceStatus"],
102
+ "changedAttributes": ["sliceStatus", "statusReason"],
95
103
  "meta": {
96
104
  "type": "SLICE_BORDER",
97
- "sliceStatus": "<newStatus>"
105
+ "sliceStatus": "<newStatus>",
106
+ "statusReason": "<one sentence: why>"
98
107
  }
99
108
  }]'
100
109
  ```
@@ -122,6 +131,7 @@ Tell the user:
122
131
  - **Slice**: the title that was updated
123
132
  - **Previous status**: `CURRENT_STATUS`
124
133
  - **New status**: `newStatus`
134
+ - **Reason**: `statusReason` (if set)
125
135
  - **Node ID**: `SLICE_NODE_ID`
126
136
  - **Outcome**: `SUCCESS`, `ALREADY_IN_STATUS` (another agent got there first — see above), or `ERROR`
127
137
  - **Any errors**: raw API message if something failed for a reason other than `ALREADY_IN_STATUS`
@@ -116,6 +116,7 @@ Rules of thumb: every API call is either a screen-driven command/read or an auto
116
116
  - `eventmodeling-designing-automation-chains` / `eventmodeling-translating-external-events` — jobs, listeners, webhooks, outbound calls;
117
117
  - `eventmodeling-elaborating-scenarios` — rules from the tests (layer 1+ only), built exactly as that skill says (see *Scenarios* below).
118
118
  Feed each skill what the lenses and the mapping table give you; ask only where the code stops (see *Step 1* when the order is unclear).
119
+ **Screens and scenarios are part of the pass — never skip them.** A chapter with only events, commands and read models is not detailed yet. Every command and read model a person uses gets its `HTML_SCREEN` (storyboarding step), and every command gets at least its happy-path scenario from `eventmodeling-elaborating-scenarios`. Never report "not done: screens, scenarios, tests, UI" and ask for the next chapter; run those steps first. If a step truly can't be done (e.g. no tests to mine for a rule), say why in the chapter's decision rows, but still place the screens and the happy-path scenario.
119
120
  2. Stay within the layer's detail budget (below). Every placement batch carries its decision rows (see *Recording decisions*).
120
121
  3. Re-space the chapters (see *Keeping chapters apart*) after every batch.
121
122
  4. Append this pass's row to the chapter's *Analysis history*.