@eventmodelers/cli 1.0.154 → 1.0.155

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
@@ -202,7 +202,7 @@ and the agent starts building — `--local` skips platform config and credential
202
202
 
203
203
  **Backend → UI handover.** Besides the usual statuses a slice can be `Ready` (modeling done, waiting to be planned) and `Handover` (backend built, UI contract published, waiting for the UI kit). The flow is `Ready → Planned → InProgress → Handover → InProgress → Done`. Install with `init --handover` (or `re-init --handover`; switch later with `eventmodelers config --handover on|off`; for a single run `run --handover` overrides the setting without saving anything), which saves `"handover": true` in `.eventmodelers/config.json` — then a backend kit ends STATE_VIEW / STATE_CHANGE slices at `Handover` instead of `Done` (the `ui-contract` skill, Step 4, owns that rule); without the flag nothing changes: slices end at `Done`, exactly as before. A kit claims work from the status named in its `lib/kit.json` — `{ "pickupStatus": "Handover" }` for the `react` / `supabase-react` UI kits, absent (= `Planned`) for backend kits. A new UI kit ships that file and sets `Done` when its half is built; a new backend kit just invokes `ui-contract`. Needs a platform with the `Ready` / `Handover` statuses.
204
204
 
205
- **UI contract.** The scaffolded `build-state-view` / `build-state-change` skills already end by invoking the shared `ui-contract` skill, which publishes each slice's read (`GET`) or write (`POST`) contract as a `## UI Contract` section in the slice's feedback-lane markdown node (exported in `slice.json` `notes[]`, so a UI build kit can look it up directly). Two things are yours to decide: *where the contract is derived from* (point the `UI Contract` section of those skills at your stack's machine-readable API description, if it has one — the `node`/`supabase` stacks use their `@openapi` blocks via a `ui-contract-from-openapi.md` reference — otherwise the skill reads the route code), and, if you need a different shape or location, a whole-skill override: ship `.claude/skills/ui-contract/SKILL.md` in the stack's templates and it replaces the shared one (stack templates are applied after `shared/skills/`).
205
+ **UI contract.** The scaffolded `build-state-view` / `build-state-change` skills already end by invoking the shared `ui-contract` skill, which publishes each slice's read (`GET`) or write (`POST`) contract — the endpoint's OpenAPI operation, as a ```` ```yaml ui-contract ```` block — as a `## UI Contract` section in the slice's feedback-lane markdown node (exported in `slice.json` `notes[]`, so a UI build kit can look it up directly). Two things are yours to decide: *where the contract is derived from* (point the `UI Contract` section of those skills at your stack's machine-readable API description, if it has one — the `node`/`supabase` stacks use their `@openapi` blocks via a `ui-contract-from-openapi.md` reference, taking the operation verbatim — otherwise the skill writes the equivalent OpenAPI operation from the route code), and, if you need a different shape or location, a whole-skill override: ship `.claude/skills/ui-contract/SKILL.md` in the stack's templates and it replaces the shared one (stack templates are applied after `shared/skills/`).
206
206
 
207
207
  Installing both a build stack and `init-modeling` into the same project reuses this one `.eventmodelers/config.json` — run whichever `init` command second and it finds the existing config already satisfies the required fields and skips straight past the credential prompt.
208
208
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventmodelers/cli",
3
- "version": "1.0.154",
3
+ "version": "1.0.155",
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": {
@@ -1,12 +1,12 @@
1
1
  ---
2
2
  name: ui-contract
3
- description: Publish the slice's UI contract (read contract = GET, write/state-change contract = POST) as its own `## UI Contract` section in the slice's MARKDOWN node in the feedback lane, which the slice export carries in `slice.json` `notes[]` — so the UI build kit finds the contract in one lookup instead of parsing the whole note. Called by every build-state-view / build-state-change skill after the endpoint is built. A build kit can override it by shipping its own `ui-contract` skill.
3
+ description: Publish the slice's UI contract — the OpenAPI operation of its endpoint (read = GET, write/state-change = POST) — as its own `## UI Contract` section in the slice's MARKDOWN node in the feedback lane, which the slice export carries in `slice.json` `notes[]` — so the UI build kit finds the contract in one lookup instead of parsing the whole note. Called by every build-state-view / build-state-change skill after the endpoint is built. A build kit can override it by shipping its own `ui-contract` skill.
4
4
  ---
5
5
 
6
6
  # UI Contract
7
7
 
8
8
  The UI build kit builds the frontend against what the backend slice **actually exposes**. This skill
9
- records that as a contract — one per slice, in a fixed, machine-findable place.
9
+ records that as a contract — the endpoint's **OpenAPI operation**, one per slice, in a fixed, machine-findable place.
10
10
 
11
11
  > Invoke `connect` first if not already connected (`BOARD_ID`). Prefer `mcp__eventmodelers__*` tools;
12
12
  > the REST equivalents are in `learn-eventmodelers-api`.
@@ -27,58 +27,24 @@ route, not the plan), before the final commit:
27
27
  | `STATE_CHANGE` (COMMAND) | **write** — `POST` (a state change) |
28
28
  | `AUTOMATION` | none — no UI counterpart, skip |
29
29
 
30
- ## Step 1 — Derive the contract from the built endpoint
30
+ ## Step 1 — Derive the OpenAPI operation from the built endpoint
31
31
 
32
- Never copy from the plan — the contract describes what was built.
32
+ Never copy from the plan — the contract describes what was built. The contract **is** the endpoint's
33
+ OpenAPI 3 operation (one path, one verb) — no second, hand-made shape.
33
34
 
34
35
  1. **Use the source the stack names.** If the calling `build-state-view` / `build-state-change`
35
- skill's `## UI Contract` section points to a more authoritative source for this stack (for example a
36
- machine-readable API description it already maintains), derive from that, mechanically.
37
- 2. **Otherwise read the endpoint's route/handler code**: method, path, parameter and field names,
38
- success status, and the error codes/statuses the handler really returns.
39
- 3. Field `type` / `optional` / `example` come from the `slice.json` element fields.
40
- 4. **Cross-check against `slice.json`**: the `body` / `response.fields` names must equal the command's /
41
- read model's `fields[]`. A mismatch is an implementation bug — fix the code, don't publish a
42
- contract that disagrees with `slice.json`.
43
-
44
- **Read contract** (`kind: "read"`):
45
-
46
- ```json
47
- {
48
- "kind": "read",
49
- "slice": "<slice title>",
50
- "sliceId": "<SLICE_BORDER node id>",
51
- "readModel": "<READMODEL title>",
52
- "method": "GET",
53
- "path": "/api/<...>",
54
- "pathParams": [{ "name": "customerId", "type": "UUID" }],
55
- "queryParams": [{ "name": "page", "type": "Int", "optional": true }],
56
- "response": {
57
- "cardinality": "Single | List",
58
- "fields": [{ "name": "status", "type": "String", "optional": false, "example": "active" }]
59
- },
60
- "errors": [{ "status": 404, "code": "not_found", "when": "no row for this id" }]
61
- }
62
- ```
63
-
64
- **Write contract** (`kind: "write"`):
65
-
66
- ```json
67
- {
68
- "kind": "write",
69
- "slice": "<slice title>",
70
- "sliceId": "<SLICE_BORDER node id>",
71
- "command": "<COMMAND title>",
72
- "method": "POST",
73
- "path": "/api/<...>",
74
- "pathParams": [],
75
- "body": [{ "name": "itemId", "type": "UUID", "optional": false, "example": "..." }],
76
- "response": { "status": 200, "body": [] },
77
- "errors": [{ "status": 400, "code": "snake_case_code", "when": "business rule violated" }]
78
- }
79
- ```
80
-
81
- Keep it to what a UI needs — no internal details (no table names, no auth plumbing beyond "requires bearer token" if `security` is set).
36
+ skill's `## UI Contract` section points to the stack's machine-readable API description (e.g. the
37
+ handler's `@openapi` block), take that operation **verbatim** — see the stack's
38
+ `ui-contract-from-openapi.md`. Do not rewrite it.
39
+ 2. **Otherwise read the endpoint's route/handler code** and write the equivalent OpenAPI operation:
40
+ method, path, `parameters` (path / query / header), `requestBody` schema, and a response for the
41
+ success status and for every error status the handler really returns.
42
+ 3. **Trim to what a UI needs**: drop `tags`, internal notes and anything about storage or auth plumbing
43
+ (keep `security` if set). Keep `summary`, `description`, parameter/field `example`s and the response
44
+ descriptions — they carry the business meaning of each error status.
45
+ 4. **Cross-check against `slice.json`**: the request-body / response property names must equal the
46
+ command's / read model's `fields[]` (path parameters such as the generated id excepted). A mismatch
47
+ is an implementation bug — fix the code, don't publish a contract that disagrees with `slice.json`.
82
48
 
83
49
  ## Step 2 — Write the `## UI Contract` section into the slice's MARKDOWN node
84
50
 
@@ -102,18 +68,27 @@ Keep it to what a UI needs — no internal details (no table names, no auth plum
102
68
 
103
69
  **READ** `GET /api/customers/{customerId}/reservation` — <one-line purpose>
104
70
 
105
- | Part | Name | Type | Optional | Example |
106
- |---|---|---|---|---|
107
- | path | customerId | UUID | no | … |
108
- | response | status | String | no | active |
109
-
110
- ```json ui-contract
111
- { ...the Step 1 JSON, verbatim... }
71
+ ```yaml ui-contract
72
+ /api/customers/{customerId}/reservation:
73
+ get:
74
+ summary: ...
75
+ parameters: [...]
76
+ responses: {...}
112
77
  ```
113
78
  ````
114
79
 
115
- The table is for humans; the fenced block whose info string is `ui-contract` is the single
116
- machine-readable source. Both must say the same thing.
80
+ `**READ**` for a `GET`, `**WRITE**` for a `POST`.
81
+
82
+ **Formatting is part of the contract.** The note renders as Markdown, so `meta.description` must contain
83
+ *real* line breaks — build the string with actual newlines (a multi-line string, or `\n` that the JSON
84
+ parser turns into a newline), never a single line and never a literal backslash-`n` in the stored text.
85
+ Blank line after the heading, blank line before and after the fenced block, the opening ```` ```yaml ui-contract ````
86
+ and the closing ```` ``` ```` each on their own line, and the YAML keeps its indentation (2 spaces). After
87
+ the write, read the node back once (`get_node`) and check that `description` has multiple lines and no
88
+ literal `\n`; if it is flat, rewrite it before moving on.
89
+
90
+ The fenced block whose info string is `ui-contract` is the single source: the operation alone, keyed by its path — no `openapi:`/`info:`/`servers:`
91
+ wrapper, no restated table.
117
92
 
118
93
  ## Step 3 — Nothing else to write
119
94
 
@@ -140,8 +115,10 @@ runner resolves `run --handover` over the project setting), else `"handover": tr
140
115
 
141
116
  1. In the slice's `slice.json`, take the `notes[]` entry that contains a `## UI Contract` heading.
142
117
  Ignore every other entry.
143
- 2. In that entry, take the fenced block whose info string is `ui-contract` (```` ```json ui-contract ````)
144
- and parse just that JSON. Ignore the rest of the note.
118
+ 2. In that entry, take the fenced block whose info string is `ui-contract` (```` ```yaml ui-contract ````)
119
+ and parse just that YAML — an OpenAPI operation keyed by its path: request = path/verb +
120
+ `parameters` + `requestBody`, success = the 2xx `responses` entry, errors = every non-2xx entry.
121
+ Ignore the rest of the note.
145
122
  3. No such entry → the backend slice isn't built yet, or the slice wasn't re-fetched since; re-fetch
146
123
  once, and if still absent build against `slice.json` and note the assumption.
147
124
 
@@ -1,17 +1,13 @@
1
- # UI Contract — derive from the `@openapi` block
1
+ # UI Contract — take the operation from the `@openapi` block
2
2
 
3
3
  Used by the `ui-contract` skill. This stack's `routes.ts` carries an `@openapi` JSDoc block above every
4
- handler (guarded by the `openapi-annotation` commit check), so derive the contract from it mechanically
5
- instead of re-reading handler logic.
4
+ handler (guarded by the `openapi-annotation` commit check). The contract **is** that operation —
5
+ publish it as is, don't translate it into another shape.
6
6
 
7
- | Contract key | OpenAPI source |
8
- |---|---|
9
- | `method` | the verb key under the path (`get` / `post`) |
10
- | `path` | the path key, verbatim (`{param}` style) |
11
- | `pathParams` / `queryParams` | `parameters[]` with `in: path` / `in: query` → `name`, `schema` (`optional` = not `required`) |
12
- | `body` (write) | `requestBody…schema.properties` |
13
- | `response.fields` (read) | the `200` response schema `properties` (follow a `$ref` to `components.schemas`); `Single` vs `List` = object vs `type: array` |
14
- | `response.status` (write) | the success response code |
15
- | `errors[]` | every non-2xx response: `status` = the key, `code` / `when` = its description / `error` example |
16
- | `type` | `string`+`uuid` → `UUID`, `integer`/`int32` → `Int`, `int64` → `Long`, `number`/`double` → `Double`, `number` → `Decimal`, `boolean` → `Boolean`, `date` / `date-time` → `Date` / `DateTime`, `object` → `Custom`, else `String` |
17
- | `example` | the schema's `example`, nothing invented |
7
+ 1. Take the YAML from the handler's `@openapi` block (strip the ` * ` comment prefix) — one path, one verb.
8
+ 2. Keep it keyed by its path (`/api/...:` → `get:`/`post:`), with `summary`, `description`, `parameters`,
9
+ `requestBody` and every `responses` entry. Follow a `$ref` into `components.schemas` and inline it, so
10
+ the operation is self-contained.
11
+ 3. Drop `tags` and anything internal. Add nothing the block doesn't say — examples come from the block.
12
+ 4. Write it as the ```` ```yaml ui-contract ```` block of the `## UI Contract` section, with real line
13
+ breaks and 2-space indentation (see the skill's formatting rule).
@@ -32,7 +32,7 @@ From the slice definition, extract:
32
32
 
33
33
  - **UI contract** — the backend slice's published write (`POST`) contract: look it up as described in the
34
34
  `ui-contract` skill ("How the UI build kit looks the contract up": the `## UI Contract` entry in
35
- `slice.json` `notes[]`, parse only its `ui-contract` block). It fixes the request: `path` (+ `pathParams`), the exact `body` fields, the success `response`, and `errors[]` (status + code) — surface each error code as its own user-facing state, not one generic failure. When absent, the backend slice isn't
35
+ `slice.json` `notes[]`, parse only its `ui-contract` block — an OpenAPI operation). It fixes the request: the path (+ path `parameters`), the exact `requestBody` properties, the 2xx `responses` entry, and every non-2xx response (status + description) — surface each error code as its own user-facing state, not one generic failure. When absent, the backend slice isn't
36
36
  built or re-fetched yet: build against `slice.json` and note the assumption in `progress.txt`.
37
37
 
38
38
  > **Comments & description**: each element carries a `comments: string[]` array (board comments) and a `description` field — use them as implementation hints, and resolve consumed comments via `POST <BASE_URL>/api/org/<ORG_ID>/boards/<BOARD_ID>/nodes/<nodeId>/comments/<commentId>/resolve`.
@@ -31,7 +31,7 @@ From the slice definition, extract:
31
31
 
32
32
  - **UI contract** — the backend slice's published read (`GET`) contract: look it up as described in the
33
33
  `ui-contract` skill ("How the UI build kit looks the contract up": the `## UI Contract` entry in
34
- `slice.json` `notes[]`, parse only its `ui-contract` block). It fixes the request: `path` (+ `pathParams`/`queryParams`), the exact `response.fields` and `Single` vs `List` cardinality, and `errors[]` — render each error status as a distinct state. When absent, the backend slice isn't
34
+ `slice.json` `notes[]`, parse only its `ui-contract` block — an OpenAPI operation). It fixes the request: the path (+ path/query `parameters`), the exact 2xx response schema (object = `Single`, `type: array` = `List`), and every non-2xx response — render each error status as a distinct state. When absent, the backend slice isn't
35
35
  built or re-fetched yet: build against `slice.json` and note the assumption in `progress.txt`.
36
36
 
37
37
  > **Comments & description**: each element carries a `comments: string[]` array (board comments) and a `description` field — use them as implementation hints, and resolve consumed comments via `POST <BASE_URL>/api/org/<ORG_ID>/boards/<BOARD_ID>/nodes/<nodeId>/comments/<commentId>/resolve`.
@@ -1,17 +1,13 @@
1
- # UI Contract — derive from the `@openapi` block
1
+ # UI Contract — take the operation from the `@openapi` block
2
2
 
3
3
  Used by the `ui-contract` skill. This stack's `routes.ts` carries an `@openapi` JSDoc block above every
4
- handler (guarded by the `openapi-annotation` commit check), so derive the contract from it mechanically
5
- instead of re-reading handler logic.
4
+ handler (guarded by the `openapi-annotation` commit check). The contract **is** that operation —
5
+ publish it as is, don't translate it into another shape.
6
6
 
7
- | Contract key | OpenAPI source |
8
- |---|---|
9
- | `method` | the verb key under the path (`get` / `post`) |
10
- | `path` | the path key, verbatim (`{param}` style) |
11
- | `pathParams` / `queryParams` | `parameters[]` with `in: path` / `in: query` → `name`, `schema` (`optional` = not `required`) |
12
- | `body` (write) | `requestBody…schema.properties` |
13
- | `response.fields` (read) | the `200` response schema `properties` (follow a `$ref` to `components.schemas`); `Single` vs `List` = object vs `type: array` |
14
- | `response.status` (write) | the success response code |
15
- | `errors[]` | every non-2xx response: `status` = the key, `code` / `when` = its description / `error` example |
16
- | `type` | `string`+`uuid` → `UUID`, `integer`/`int32` → `Int`, `int64` → `Long`, `number`/`double` → `Double`, `number` → `Decimal`, `boolean` → `Boolean`, `date` / `date-time` → `Date` / `DateTime`, `object` → `Custom`, else `String` |
17
- | `example` | the schema's `example`, nothing invented |
7
+ 1. Take the YAML from the handler's `@openapi` block (strip the ` * ` comment prefix) — one path, one verb.
8
+ 2. Keep it keyed by its path (`/api/...:` → `get:`/`post:`), with `summary`, `description`, `parameters`,
9
+ `requestBody` and every `responses` entry. Follow a `$ref` into `components.schemas` and inline it, so
10
+ the operation is self-contained.
11
+ 3. Drop `tags` and anything internal. Add nothing the block doesn't say — examples come from the block.
12
+ 4. Write it as the ```` ```yaml ui-contract ```` block of the `## UI Contract` section, with real line
13
+ breaks and 2-space indentation (see the skill's formatting rule).
@@ -56,9 +56,9 @@ Extract:
56
56
  - `description` — implementation hints (validation rules, business constraints).
57
57
  - **UI contract (write)** — the backend slice's published `POST` contract: look it up as described in
58
58
  the `ui-contract` skill ("How the UI build kit looks the contract up" — the `## UI Contract` entry in
59
- `slice.json` `notes[]`; parse only its `ui-contract` block). When present it **overrides
60
- `apiEndpoint`** and fixes everything the component must honour: `path` (+ `pathParams`), the exact
61
- `body` fields, the success `response`, and `errors[]` (status + code) — surface each error code as a
59
+ `slice.json` `notes[]`; parse only its `ui-contract` block — an OpenAPI operation). When present it **overrides
60
+ `apiEndpoint`** and fixes everything the component must honour: the path (+ path `parameters`), the exact
61
+ `requestBody` properties, the 2xx `responses` entry, and every non-2xx response (status + description) — surface each error code as a
62
62
  distinct user-facing state through `onError`, not one generic failure. When absent, the backend slice
63
63
  isn't built or re-fetched yet: fall back to `apiEndpoint` / `fields[]` as above and note the assumption
64
64
  in `progress.txt`. If contract and `command.fields[]` disagree, the contract wins for the request and