@eventmodelers/cli 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/README.md +175 -0
  2. package/cli.js +676 -0
  3. package/package.json +35 -0
  4. package/shared/build-kit/code-export.mjs +560 -0
  5. package/shared/build-kit/lib/ollama-agent.js +147 -0
  6. package/shared/build-kit/package.json +11 -0
  7. package/shared/build-kit/ralph-ollama.js +39 -0
  8. package/shared/build-kit/realtime-agent.js +18 -0
  9. package/stacks/axon/templates/.claude/skills/build-automation/SKILL.md +407 -0
  10. package/stacks/axon/templates/.claude/skills/build-automation/references/feature-flag-patterns.md +114 -0
  11. package/stacks/axon/templates/.claude/skills/build-automation-workflow/SKILL.md +496 -0
  12. package/stacks/axon/templates/.claude/skills/build-automation-workflow/references/axon-workflow-api.md +279 -0
  13. package/stacks/axon/templates/.claude/skills/build-automation-workflow/references/feature-flag-patterns.md +114 -0
  14. package/stacks/axon/templates/.claude/skills/build-state-change/SKILL.md +396 -0
  15. package/stacks/axon/templates/.claude/skills/build-state-change/references/axon-test-fixture-patterns.md +162 -0
  16. package/stacks/axon/templates/.claude/skills/build-state-change/references/feature-flag-patterns.md +56 -0
  17. package/stacks/axon/templates/.claude/skills/build-state-change/references/rest-api-patterns.md +115 -0
  18. package/stacks/axon/templates/.claude/skills/build-state-view/SKILL.md +282 -0
  19. package/stacks/axon/templates/.claude/skills/connect/SKILL.md +178 -0
  20. package/stacks/axon/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +611 -0
  21. package/stacks/axon/templates/.claude/skills/load-slice/SKILL.md +141 -0
  22. package/stacks/axon/templates/.claude/skills/update-slice-status/SKILL.md +105 -0
  23. package/stacks/axon/templates/build-kit/lib/AGENT.md +47 -0
  24. package/stacks/axon/templates/build-kit/lib/agent.sh +20 -0
  25. package/stacks/axon/templates/build-kit/lib/backend-prompt.md +158 -0
  26. package/stacks/axon/templates/build-kit/lib/prompt.md +126 -0
  27. package/stacks/axon/templates/build-kit/lib/ralph.js +369 -0
  28. package/stacks/axon/templates/build-kit/ralph-claude.js +44 -0
  29. package/stacks/axon/templates/build-kit/ralph.sh +98 -0
  30. package/stacks/axon/templates/root/.env.example +5 -0
  31. package/stacks/axon/templates/root/CLAUDE.md +60 -0
  32. package/stacks/axon/templates/root/README.md +44 -0
  33. package/stacks/axon/templates/root/docker-compose.yml +64 -0
  34. package/stacks/axon/templates/root/mvnw +259 -0
  35. package/stacks/axon/templates/root/mvnw.cmd +149 -0
  36. package/stacks/axon/templates/root/pom.xml +125 -0
  37. package/stacks/axon/templates/root/src/main/java/io/axoniq/quickstart/QuickstartApplication.java +128 -0
  38. package/stacks/axon/templates/root/src/main/java/io/axoniq/quickstart/config/WebConfig.java +155 -0
  39. package/stacks/axon/templates/root/src/main/resources/application.properties +2 -0
  40. package/stacks/axon/templates/root/src/main/resources/static/bg-body.png +0 -0
  41. package/stacks/axon/templates/root/src/main/resources/static/bg-header.png +0 -0
  42. package/stacks/axon/templates/root/src/main/resources/static/index.html +596 -0
  43. package/stacks/cratis-csharp/templates/.claude/skills/_shared/cratis-conventions.md +251 -0
  44. package/stacks/cratis-csharp/templates/.claude/skills/build-automation/SKILL.md +99 -0
  45. package/stacks/cratis-csharp/templates/.claude/skills/build-automation/references/patterns.md +115 -0
  46. package/stacks/cratis-csharp/templates/.claude/skills/build-state-change/SKILL.md +163 -0
  47. package/stacks/cratis-csharp/templates/.claude/skills/build-state-change/references/patterns.md +234 -0
  48. package/stacks/cratis-csharp/templates/.claude/skills/build-state-view/SKILL.md +120 -0
  49. package/stacks/cratis-csharp/templates/.claude/skills/build-state-view/references/patterns.md +166 -0
  50. package/stacks/cratis-csharp/templates/.claude/skills/connect/SKILL.md +169 -0
  51. package/stacks/cratis-csharp/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +609 -0
  52. package/stacks/cratis-csharp/templates/.claude/skills/load-slice/SKILL.md +141 -0
  53. package/stacks/cratis-csharp/templates/.claude/skills/update-slice-status/SKILL.md +105 -0
  54. package/stacks/cratis-csharp/templates/build-kit/lib/AGENT.md +58 -0
  55. package/stacks/cratis-csharp/templates/build-kit/lib/agent.sh +20 -0
  56. package/stacks/cratis-csharp/templates/build-kit/lib/backend-prompt.md +129 -0
  57. package/stacks/cratis-csharp/templates/build-kit/lib/prompt.md +124 -0
  58. package/stacks/cratis-csharp/templates/build-kit/lib/ralph.js +302 -0
  59. package/stacks/cratis-csharp/templates/build-kit/ralph-claude.js +37 -0
  60. package/stacks/cratis-csharp/templates/build-kit/ralph.sh +98 -0
  61. package/stacks/cratis-csharp/templates/root/.frontend/index.css +29 -0
  62. package/stacks/cratis-csharp/templates/root/.frontend/index.html +17 -0
  63. package/stacks/cratis-csharp/templates/root/.frontend/main.tsx +18 -0
  64. package/stacks/cratis-csharp/templates/root/.frontend/tsconfig.json +42 -0
  65. package/stacks/cratis-csharp/templates/root/.frontend/tsconfig.node.json +11 -0
  66. package/stacks/cratis-csharp/templates/root/.frontend/vite.config.ts +56 -0
  67. package/stacks/cratis-csharp/templates/root/App.tsx +23 -0
  68. package/stacks/cratis-csharp/templates/root/CLAUDE.md +62 -0
  69. package/stacks/cratis-csharp/templates/root/CratisApp.csproj +25 -0
  70. package/stacks/cratis-csharp/templates/root/CratisApp.sln +18 -0
  71. package/stacks/cratis-csharp/templates/root/GlobalUsings.cs +3 -0
  72. package/stacks/cratis-csharp/templates/root/Home.tsx +102 -0
  73. package/stacks/cratis-csharp/templates/root/Program.cs +26 -0
  74. package/stacks/cratis-csharp/templates/root/README.md +192 -0
  75. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/AllListings.ts +47 -0
  76. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/Listing.cs +11 -0
  77. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/Listing.ts +12 -0
  78. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/ListingDataTable.tsx +17 -0
  79. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/index.ts +1 -0
  80. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Registration/Register.ts +51 -0
  81. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Registration/RegisterDialog.tsx +18 -0
  82. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Registration/Registration.cs +27 -0
  83. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Registration/index.ts +1 -0
  84. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/SomeFeature.tsx +22 -0
  85. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/SomeName.cs +3 -0
  86. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/index.ts +1 -0
  87. package/stacks/cratis-csharp/templates/root/appsettings.Development.json +9 -0
  88. package/stacks/cratis-csharp/templates/root/appsettings.json +26 -0
  89. package/stacks/cratis-csharp/templates/root/docker-compose.yml +23 -0
  90. package/stacks/cratis-csharp/templates/root/package.json +33 -0
  91. package/stacks/cratis-csharp/templates/root/tsconfig.json +3 -0
  92. package/stacks/modeling-kit/templates/.claude/settings.local.json +13 -0
  93. package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/SKILL.md +215 -0
  94. package/stacks/modeling-kit/templates/.claude/skills/attributes/SKILL.md +176 -0
  95. package/stacks/modeling-kit/templates/.claude/skills/connect/SKILL.md +182 -0
  96. package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/SKILL.md +383 -0
  97. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-applying-conways-law/SKILL.md +465 -0
  98. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/SKILL.md +587 -0
  99. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/references/facilitating-event-modeling-workshops.md +626 -0
  100. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/SKILL.md +542 -0
  101. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/SKILL.md +497 -0
  102. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +783 -0
  103. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/SKILL.md +604 -0
  104. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md +699 -0
  105. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-integrating-legacy-systems/SKILL.md +674 -0
  106. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/SKILL.md +231 -0
  107. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/domain-patterns.md +150 -0
  108. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/patterns.md +329 -0
  109. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/snapshotting.md +204 -0
  110. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/SKILL.md +352 -0
  111. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/references/project-planning-with-event-modeling.md +341 -0
  112. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/SKILL.md +153 -0
  113. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/SKILL.md +136 -0
  114. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/examples.md +78 -0
  115. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/patterns.md +85 -0
  116. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/SKILL.md +655 -0
  117. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-translating-external-events/SKILL.md +479 -0
  118. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/SKILL.md +234 -0
  119. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/SKILL.md +353 -0
  120. package/stacks/modeling-kit/templates/.claude/skills/examples/SKILL.md +200 -0
  121. package/stacks/modeling-kit/templates/.claude/skills/handle-comment/SKILL.md +94 -0
  122. package/stacks/modeling-kit/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +441 -0
  123. package/stacks/modeling-kit/templates/.claude/skills/place-element/SKILL.md +290 -0
  124. package/stacks/modeling-kit/templates/.claude/skills/storyboard/SKILL.md +212 -0
  125. package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/SKILL.md +123 -0
  126. package/stacks/modeling-kit/templates/.claude/skills/timeline/SKILL.md +353 -0
  127. package/stacks/modeling-kit/templates/.claude/skills/update-slice-status/SKILL.md +110 -0
  128. package/stacks/modeling-kit/templates/.claude/skills/wdyt/SKILL.md +134 -0
  129. package/stacks/modeling-kit/templates/kit/lib/agent.sh +20 -0
  130. package/stacks/modeling-kit/templates/kit/lib/ollama-agent.js +147 -0
  131. package/stacks/modeling-kit/templates/kit/lib/ralph.js +286 -0
  132. package/stacks/modeling-kit/templates/kit/package.json +12 -0
  133. package/stacks/modeling-kit/templates/kit/ralph-claude.js +50 -0
  134. package/stacks/modeling-kit/templates/kit/ralph-ollama.js +38 -0
  135. package/stacks/modeling-kit/templates/kit/ralph.sh +61 -0
  136. package/stacks/modeling-kit/templates/kit/realtime-agent.js +18 -0
  137. package/stacks/modeling-kit/templates/root/CLAUDE.md +56 -0
  138. package/stacks/node/templates/.claude/skills/build-automation/SKILL.md +266 -0
  139. package/stacks/node/templates/.claude/skills/build-state-change/SKILL.md +344 -0
  140. package/stacks/node/templates/.claude/skills/build-state-view/SKILL.md +412 -0
  141. package/stacks/node/templates/.claude/skills/connect/SKILL.md +178 -0
  142. package/stacks/node/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +628 -0
  143. package/stacks/node/templates/.claude/skills/load-slice/SKILL.md +143 -0
  144. package/stacks/node/templates/.claude/skills/update-slice-status/SKILL.md +110 -0
  145. package/stacks/node/templates/build-kit/README.md +86 -0
  146. package/stacks/node/templates/build-kit/lib/AGENT.md +47 -0
  147. package/stacks/node/templates/build-kit/lib/agent.sh +20 -0
  148. package/stacks/node/templates/build-kit/lib/backend-prompt.md +152 -0
  149. package/stacks/node/templates/build-kit/lib/prompt.md +122 -0
  150. package/stacks/node/templates/build-kit/lib/ralph.js +369 -0
  151. package/stacks/node/templates/build-kit/ralph-claude.js +44 -0
  152. package/stacks/node/templates/build-kit/ralph.sh +98 -0
  153. package/stacks/node/templates/root/.env.example +15 -0
  154. package/stacks/node/templates/root/CLAUDE.md +60 -0
  155. package/stacks/node/templates/root/docker-compose.yml +15 -0
  156. package/stacks/node/templates/root/flyway.conf +17 -0
  157. package/stacks/node/templates/root/migrations/V1__schema.sql.example +12 -0
  158. package/stacks/node/templates/root/package.json +50 -0
  159. package/stacks/node/templates/root/server.ts +130 -0
  160. package/stacks/node/templates/root/setup-env.sh +53 -0
  161. package/stacks/node/templates/root/src/common/assertions.ts +6 -0
  162. package/stacks/node/templates/root/src/common/db.ts +32 -0
  163. package/stacks/node/templates/root/src/common/loadPostgresEventstore.ts +23 -0
  164. package/stacks/node/templates/root/src/common/parseEndpoint.ts +51 -0
  165. package/stacks/node/templates/root/src/common/processorDlq.ts +28 -0
  166. package/stacks/node/templates/root/src/common/realtimeBroadcast.ts +12 -0
  167. package/stacks/node/templates/root/src/common/replay.ts +16 -0
  168. package/stacks/node/templates/root/src/common/routes.ts +19 -0
  169. package/stacks/node/templates/root/src/common/testHelpers.ts +44 -0
  170. package/stacks/node/templates/root/src/swagger.ts +34 -0
  171. package/stacks/node/templates/root/src/util/assertions.ts +6 -0
  172. package/stacks/node/templates/root/src/util/hash.ts +9 -0
  173. package/stacks/node/templates/root/src/util/sanitize.ts +23 -0
  174. package/stacks/node/templates/root/tsconfig.json +32 -0
  175. package/stacks/node/templates/root/vercel.json +8 -0
  176. package/stacks/supabase/templates/.claude/skills/build-automation/SKILL.md +266 -0
  177. package/stacks/supabase/templates/.claude/skills/build-state-change/SKILL.md +344 -0
  178. package/stacks/supabase/templates/.claude/skills/build-state-view/SKILL.md +390 -0
  179. package/stacks/supabase/templates/.claude/skills/build-webhook/SKILL.md +303 -0
  180. package/stacks/supabase/templates/.claude/skills/connect/SKILL.md +178 -0
  181. package/stacks/supabase/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +628 -0
  182. package/stacks/supabase/templates/.claude/skills/load-slice/SKILL.md +143 -0
  183. package/stacks/supabase/templates/.claude/skills/update-slice-status/SKILL.md +110 -0
  184. package/stacks/supabase/templates/build-kit/README.md +86 -0
  185. package/stacks/supabase/templates/build-kit/lib/AGENT.md +47 -0
  186. package/stacks/supabase/templates/build-kit/lib/agent.sh +20 -0
  187. package/stacks/supabase/templates/build-kit/lib/backend-prompt.md +152 -0
  188. package/stacks/supabase/templates/build-kit/lib/prompt.md +123 -0
  189. package/stacks/supabase/templates/build-kit/lib/ralph.js +369 -0
  190. package/stacks/supabase/templates/build-kit/ralph-claude.js +44 -0
  191. package/stacks/supabase/templates/build-kit/ralph.sh +98 -0
  192. package/stacks/supabase/templates/root/.env.example +22 -0
  193. package/stacks/supabase/templates/root/CLAUDE.md +61 -0
  194. package/stacks/supabase/templates/root/flyway.conf +17 -0
  195. package/stacks/supabase/templates/root/package.json +52 -0
  196. package/stacks/supabase/templates/root/server.ts +177 -0
  197. package/stacks/supabase/templates/root/setup-env.sh +53 -0
  198. package/stacks/supabase/templates/root/src/common/assertions.ts +6 -0
  199. package/stacks/supabase/templates/root/src/common/db.ts +32 -0
  200. package/stacks/supabase/templates/root/src/common/loadPostgresEventstore.ts +23 -0
  201. package/stacks/supabase/templates/root/src/common/parseEndpoint.ts +51 -0
  202. package/stacks/supabase/templates/root/src/common/processorDlq.ts +28 -0
  203. package/stacks/supabase/templates/root/src/common/realtimeBroadcast.ts +19 -0
  204. package/stacks/supabase/templates/root/src/common/replay.ts +16 -0
  205. package/stacks/supabase/templates/root/src/common/routes.ts +23 -0
  206. package/stacks/supabase/templates/root/src/common/testHelpers.ts +54 -0
  207. package/stacks/supabase/templates/root/src/supabase/README.md +141 -0
  208. package/stacks/supabase/templates/root/src/supabase/api.ts +56 -0
  209. package/stacks/supabase/templates/root/src/supabase/requireOrgaAdmin.ts +32 -0
  210. package/stacks/supabase/templates/root/src/supabase/requireSysUser.ts +40 -0
  211. package/stacks/supabase/templates/root/src/supabase/requireUser.ts +72 -0
  212. package/stacks/supabase/templates/root/src/swagger.ts +34 -0
  213. package/stacks/supabase/templates/root/src/util/assertions.ts +6 -0
  214. package/stacks/supabase/templates/root/src/util/hash.ts +9 -0
  215. package/stacks/supabase/templates/root/src/util/sanitize.ts +23 -0
  216. package/stacks/supabase/templates/root/supabase/config.toml +295 -0
  217. package/stacks/supabase/templates/root/supabase/migrations/V1__schema.sql.example +12 -0
  218. package/stacks/supabase/templates/root/supabase/seed.sql +1 -0
  219. package/stacks/supabase/templates/root/tsconfig.json +32 -0
  220. package/stacks/supabase/templates/root/vercel.json +8 -0
@@ -0,0 +1,290 @@
1
+ ---
2
+ name: place-element
3
+ description: Place a COMMAND, READMODEL, EVENT, SCREEN, AUTOMATION, or SCENARIO spec node onto an existing eventmodelers board timeline at a specific position
4
+ ---
5
+
6
+ # Place Element
7
+
8
+ > **Before doing anything else**, invoke the `connect` skill to resolve `TOKEN`, `BOARD_ID`, and `BASE_URL`. Then invoke the `learn-eventmodelers-api` skill to load the full API reference. Do not proceed until both skills have been loaded.
9
+
10
+ Place a single element — COMMAND, READMODEL, EVENT, SCREEN, AUTOMATION, or SCENARIO spec node — onto an existing timeline on an eventmodelers board. Uses an existing column when a position is given; only creates a new column when appending.
11
+ ---
12
+
13
+ ## Step 1 — Parse arguments
14
+
15
+ From `$ARGUMENTS`, extract:
16
+
17
+ | Field | How to find it | Default |
18
+ |-------|---------------|---------|
19
+ | `elementType` | `event`, `command`, `readmodel`, `screen`, or `automation` (case-insensitive) | **required** |
20
+ | `title` | the element name, e.g. "Order Placed" | **required** |
21
+ | `boardId` | a board UUID | from `connect` skill (`BOARD_ID`) |
22
+ | `timelineId` | the chapter/timeline UUID | auto-detect (see Step 2) |
23
+ | `position` | column index (0-based number), `"after <title>"`, or omitted | append at end |
24
+ | `baseUrl` | explicit URL override | from `connect` skill (`BASE_URL`) |
25
+
26
+ Normalise `elementType` to uppercase: `event` → `EVENT`, `command` → `COMMAND`, `readmodel` → `READMODEL`, `screen` → `SCREEN`, `automation` → `AUTOMATION`.
27
+
28
+ Use `BOARD_ID` and `BASE_URL` from the `connect` skill. If a `boardId` argument is explicitly passed, it overrides `BOARD_ID`.
29
+
30
+ ---
31
+
32
+ ## Step 2 — Resolve the timeline
33
+
34
+ If `timelineId` is not provided, discover chapters on the board:
35
+
36
+ ```bash
37
+ curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=CHAPTER"
38
+ ```
39
+
40
+ - **Exactly one chapter** → use it automatically, tell the user which one was selected.
41
+ - **Multiple chapters** → list them by name/ID and ask the user which to target.
42
+ - **No chapters** → stop and tell the user to create a chapter first (e.g. via the `/timeline` skill).
43
+
44
+ ---
45
+
46
+ ## Step 3 — Fetch existing columns and resolve position
47
+
48
+ Always fetch the chapter node first to get the current timeline state:
49
+
50
+ ```bash
51
+ curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$TIMELINE_ID"
52
+ ```
53
+
54
+ From `meta.timelineData`, read `columns` (ordered array of column objects with `id` and `index`) and `cells`.
55
+
56
+ Then resolve `position`:
57
+
58
+ | Input | Behaviour |
59
+ |-------|-----------|
60
+ | A number (e.g. `2`) | Find the existing column whose `index === 2`. Save its `id` as `columnId`. **Do NOT create a new column.** |
61
+ | `"after <title>"` | Cross-reference node titles to find the named column, then target the column at position + 1. If that next column already exists use it; if not, create one at that index. |
62
+ | Omitted | No existing column is targeted → create a new column at the end (Step 5). |
63
+
64
+ If `position` is a number and no column exists at that index, stop and tell the user: "No column at index `<n>` — did you mean to append instead?"
65
+
66
+ ---
67
+
68
+ ## Step 4 — Determine the target lane
69
+
70
+ | `elementType` | Target lane `type` |
71
+ |---------------|--------------------|
72
+ | `EVENT` | `swimlane` |
73
+ | `COMMAND` | `interaction` |
74
+ | `READMODEL` | `interaction` |
75
+ | `SCREEN` | `actor` |
76
+ | `AUTOMATION` | `actor` |
77
+ | `SCENARIO` | `spec` |
78
+
79
+ ---
80
+
81
+ ## Step 4a — SCENARIO only: append scenarios via the spec endpoint
82
+
83
+ **Only applies when `elementType === "SCENARIO"`.**
84
+
85
+ The `/scenarios` endpoint creates the SCENARIO spec node automatically if the spec cell is empty, then appends all provided scenarios in one call. Do **not** use `/nodes/events` to create the spec node or write scenarios manually.
86
+
87
+ After resolving `columnId` (from step 3, or from step 5 if a new column was just created), call:
88
+
89
+ ```bash
90
+ curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/timelines/$TL/columns/$COL/scenarios" \
91
+ -H "x-token: $TOKEN" -H "Content-Type: application/json" \
92
+ -d '[
93
+ {
94
+ "id": "<scenario-uuid>",
95
+ "title": "Happy path",
96
+ "given": [{"id":"<eventNodeId>","title":"OrderPlaced","type":"EVENT"}],
97
+ "when": [{"id":"<commandNodeId>","title":"PlaceOrder","type":"COMMAND"}],
98
+ "then": [{"id":"<eventNodeId2>","title":"OrderConfirmed","type":"EVENT"}]
99
+ },
100
+ {
101
+ "id": "<scenario-uuid>",
102
+ "title": "Insufficient stock",
103
+ "given": [{"id":"<eventNodeId>","title":"OrderPlaced","type":"EVENT"}],
104
+ "when": [{"id":"<commandNodeId>","title":"PlaceOrder","type":"COMMAND"}],
105
+ "then": [],
106
+ "expectError": true,
107
+ "errorDescription": "Stock below requested quantity"
108
+ }
109
+ ]'
110
+ # → 201 { specNodeId, scenarios (all), added (count), isNewNode }
111
+ ```
112
+
113
+ **Scenario object shapes:**
114
+
115
+ | Scenario type | Shape |
116
+ |---|---|
117
+ | Happy path | `{ id, title, given[], when[], then[] }` |
118
+ | Error case | `{ id, title, given[], when[], then: [], expectError: true, errorDescription: "..." }` |
119
+
120
+ **Step item format** — each item in `given`, `when`, `then`:
121
+ ```json
122
+ { "id": "<board-node-uuid>", "title": "OrderPlaced", "type": "EVENT", "fields": [], "specRow": 0 }
123
+ ```
124
+ `id` is required (board node UUID). `title` and `type` are informational.
125
+
126
+ | Step | Allowed types |
127
+ |------|--------------|
128
+ | `given` | `EVENT` only |
129
+ | `when` | at most one `COMMAND`; empty when `then` has a READMODEL |
130
+ | `then` (happy path) | `EVENT` or `READMODEL` — not mixed |
131
+ | `then` (error case) | leave empty, use `expectError: true` |
132
+
133
+ **Mapping config IDs to board node IDs** — if working from a slice config file, build a lookup first:
134
+ ```
135
+ config_command_id → board COMMAND node ID (from chapter cells for that column)
136
+ config_event_id → board EVENT node ID (from chapter cells for that column)
137
+ config_rm_id → board READMODEL node ID
138
+ ```
139
+
140
+ Once `/scenarios` returns `201`, proceed directly to **Step 8** — report back to the user.
141
+
142
+ ---
143
+
144
+ ## Step 5 — Create a column only when appending
145
+
146
+ **Skip this step entirely** when `columnId` was already resolved in Step 3 (i.e. the user targeted an existing column).
147
+
148
+ Only run this when position was omitted (append mode):
149
+
150
+ ```bash
151
+ curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/timelines/$TIMELINE_ID/columns" \
152
+ -H "x-token: $TOKEN" \
153
+ -H "x-board-id: $BOARD_ID" \
154
+ -H "x-user-id: agent" \
155
+ -H "Content-Type: application/json" \
156
+ -d '{}'
157
+ ```
158
+
159
+ Response: `{ "columnId": "<uuid>", "index": <n>, "totalColumns": <n> }`
160
+
161
+ Save `columnId` from the response.
162
+
163
+ ---
164
+
165
+ ## Step 6 — Compute the target cell ID and check availability
166
+
167
+ Using the `timelineData` already fetched in Step 3 (re-fetch if a column was just created):
168
+
169
+ - Find the row in `rows` whose `type` matches the target lane (`swimlane`, `interaction`, or `actor`).
170
+ - Compute the cell ID directly: **`CELL_ID = targetRow.id + "-" + columnId`**
171
+
172
+ Cell IDs are always `<rowId>-<columnId>` — no cell array search needed.
173
+
174
+ **Check if the cell is already occupied**: query nodes in that cell:
175
+
176
+ ```bash
177
+ curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=$CELL_ID"
178
+ ```
179
+
180
+ **If the cell is occupied**, the behaviour depends on the element type being placed:
181
+
182
+ | Element type | Occupant type in same cell | Action |
183
+ |---|---|---|
184
+ | `READMODEL` | `COMMAND` (state-change slice already owns this column) | Insert a **new column immediately after** the current column (not at the end) and use that new column as the target. |
185
+ | `SCREEN` (view/output screen) | any | Same as READMODEL — insert immediately after. |
186
+ | Any | Same element type | Stop and tell the user — true conflict, no safe default. |
187
+ | Any | Different type but not a known pairing | Stop and tell the user. |
188
+
189
+ **Insert immediately after** means: create the new column with `index = currentColumnIndex + 1`, not by appending to the end. This keeps the read model visually adjacent to the event that drives it.
190
+
191
+ ```bash
192
+ curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/timelines/$TIMELINE_ID/columns" \
193
+ -H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" -H "x-user-id: agent" \
194
+ -H "Content-Type: application/json" \
195
+ -d '{"index": <currentColumnIndex + 1>}'
196
+ ```
197
+
198
+ If no matching row is found, stop and report the error — the timeline may be missing the required lane type.
199
+
200
+ ---
201
+
202
+ ## Step 7 — Create the node
203
+
204
+ > **SCREEN nodes always require a sketch.** The server auto-generates a title-only placeholder when a SCREEN is created without one, but it will look empty. Always follow up a SCREEN node creation with a call to `POST /images/:nodeId/sketch` (or use `POST /image-nodes/:nodeId/sketch` to create and render in one call — see `learn-eventmodelers-api` for the sketch format).
205
+
206
+ Include `x-token`, `x-board-id`, and `x-user-id: agent` on every call to `/nodes/events`:
207
+
208
+ ```bash
209
+ curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/events" \
210
+ -H "x-token: $TOKEN" \
211
+ -H "x-board-id: $BOARD_ID" \
212
+ -H "x-user-id: agent" \
213
+ -H "Content-Type: application/json" \
214
+ -d '[{
215
+ "eventType": "node:created",
216
+ "nodeId": "<node-uuid>",
217
+ "boardId": "<BOARD_ID>",
218
+ "timestamp": <Date.now()>,
219
+ "chapterId": "<TIMELINE_ID>",
220
+ "cellId": "<CELL_ID>",
221
+ "meta": {
222
+ "type": "<ELEMENT_TYPE>",
223
+ "title": "<title>"
224
+ },
225
+ "node": { "data": { "title": "<title>" } }
226
+ }]'
227
+ ```
228
+
229
+ Response: `{ "hashes": { "<event-uuid>": "<hash>" } }`
230
+
231
+ > **`node:created` with `cellId` IS the placement** — do NOT also call the `drop` endpoint afterwards. The `drop` endpoint adds a second cell reference without removing the first, causing the node to appear in two columns simultaneously. Use `node:created + cellId` for all initial placements.
232
+
233
+ ---
234
+
235
+ ## Step 8 — Report back
236
+
237
+ Tell the user:
238
+
239
+ - **What was placed**: element type and title
240
+ - **Where**: column index on the timeline
241
+ - **Node ID**: the UUID of the placed element
242
+ - **Cell ID**: the cell it was placed into
243
+ - **Any errors**: raw API message if something failed
244
+
245
+ Example success output:
246
+ ```
247
+ Placed: EVENT "Order Placed" at column 3
248
+ Node ID: a1b2c3d4-…
249
+ Cell ID: e5f6g7h8-…
250
+ Timeline: <timelineId>
251
+ ```
252
+
253
+ ---
254
+
255
+ ## Example — place an EVENT via curl
256
+
257
+ Full working example placing an EVENT called "Order Placed" at the end of a timeline:
258
+
259
+ ```bash
260
+ # 1. Add a column (append at end)
261
+ curl -s -X POST "http://localhost:3000/api/org/<ORG_ID>/boards/<BOARD_ID>/timelines/<TIMELINE_ID>/columns" \
262
+ -H "Content-Type: application/json" \
263
+ -d '{}'
264
+
265
+ # 2. Fetch chapter to find the target lane cell for the new column
266
+ curl -s -H "x-user-id: place-element-skill" \
267
+ "http://localhost:3000/api/org/<ORG_ID>/boards/<BOARD_ID>/nodes/<TIMELINE_ID>"
268
+
269
+ # 3. Create the EVENT node
270
+
271
+ Do not skip the User-ID.
272
+
273
+
274
+ curl -s -X POST "http://localhost:3000/api/org/<ORG_ID>/boards/<BOARD_ID>/nodes/events" \
275
+ -H "Content-Type: application/json" \
276
+ -H "x-user-id: place-element-skill" \
277
+ -d '[{
278
+ "id": "<event-uuid>",
279
+ "eventType": "node:created",
280
+ "nodeId": "<node-uuid>",
281
+ "boardId": "<BOARD_ID>",
282
+ "timestamp": 1714900000000,
283
+ "chapterId": "<TIMELINE_ID>",
284
+ "cellId": "<CELL_ID>",
285
+ "meta": { "type": "EVENT", "title": "Order Placed" },
286
+ "node": { "id": "<node-uuid>", "data": { "title": "Order Placed" } }
287
+ }]'
288
+ ```
289
+
290
+ Replace `<TIMELINE_ID>`, `<BOARD_ID>`, `<CELL_ID>`, `<event-uuid>`, and `<node-uuid>` with real UUIDs. Use `Date.now()` or a current unix-ms timestamp for `timestamp`.
@@ -0,0 +1,212 @@
1
+ ---
2
+ name: storyboard
3
+ description: Build a complete visual storyboard with AI-generated screens from a natural language description — creates a chapter, N columns, and N custom sketch screens
4
+ ---
5
+
6
+ # Storyboard Builder
7
+
8
+ > **Before doing anything else**, invoke the `connect` skill to resolve `TOKEN`, `BOARD_ID`, and `BASE_URL`. Do not proceed until the connect skill has completed.
9
+
10
+ You are building a complete visual storyboard by calling the board HTTP API. You generate the screen designs yourself using the grid description language below, then create the storyboard structure via `curl`. Only SCREEN nodes are created — no COMMAND or EVENT nodes.
11
+
12
+ ## Step 1 — Parse arguments
13
+
14
+ From `$ARGUMENTS`, extract:
15
+
16
+ | Field | How to find it | Default |
17
+ |-------|---------------|---------|
18
+ | `description` | the flow name, e.g. "login flow", "checkout", "user onboarding" | required |
19
+ | `screenCount` | any number mentioned, e.g. "6 screens", "with 4 steps" | 3 |
20
+ | `boardId` | a board ID string, e.g. "board-abc" | from `connect` skill (`BOARD_ID`) |
21
+ | `chapterId` | an existing chapter ID to reuse, e.g. "chapter-xyz" | empty — create a new chapter |
22
+ | `baseUrl` | explicit URL override | from `connect` skill (`BASE_URL`) |
23
+
24
+ `BOARD_ID` and `BASE_URL` come from the `connect` skill. Only ask the user for `boardId` if explicitly overriding.
25
+ If `chapterId` is provided, skip chapter creation and go straight to Step 4.
26
+
27
+ ## Step 2 — Plan all screens and create tasks
28
+
29
+ Before making any API calls, plan all N screens. For each screen, decide:
30
+
31
+ - `screenTitle` — human-readable name (e.g. "Enter Credentials")
32
+ - `elements` — a minimal list of grid elements (see language below, aim for 5–8 elements)
33
+ - `visualDescription` — a prose description of the screen's visual layout and content (2–4 sentences) that lets someone who cannot see the image understand what is shown: what UI sections appear, what text/labels are visible, where buttons and inputs are placed, and the overall purpose of the screen
34
+
35
+ Then **create one task per screen** using TaskCreate, naming each task after the screen title. This gives you a visible queue of work. Create the screens directly after each task has been planned.
36
+
37
+ ## Grid description language
38
+
39
+ Canvas: **50 × 40 grid units** (1000 × 800 px, 1 unit = 20 px).
40
+
41
+ **Keep screens simple.** 5–8 elements is ideal. Speed matters more than detail — use rectangles as section placeholders, skip decorative elements. Skip circles.
42
+
43
+ Every screen's `elements` array **must start** with a full white background:
44
+ ```json
45
+ {"type":"rectangle","gridX":0,"gridY":0,"gridWidth":50,"gridHeight":40,"fill":"white"}
46
+ ```
47
+
48
+ ### Element types
49
+
50
+ | type | required fields | optional fields |
51
+ |------|----------------|-----------------|
52
+ | `rectangle` | gridX, gridY, gridWidth, gridHeight | fill, stroke |
53
+ | `text` | gridX, gridY, text | fontSize (default 12), fill, gridWidth |
54
+ | `headline` | gridX, gridY, text | fontSize (default 20), fill, gridWidth |
55
+ | `button` | gridX, gridY, gridWidth, gridHeight, text | fill, stroke |
56
+ | `input` | gridX, gridY, gridWidth, gridHeight, text (placeholder) | fill, stroke |
57
+ | `image` | gridX, gridY, gridWidth, gridHeight | fill (placeholder color) |
58
+ | `line` | gridX, gridY, gridX2, gridY2 | stroke |
59
+ | `circle` | gridX, gridY, gridRadius | fill, stroke |
60
+
61
+ ### Colors
62
+ Limited to: `black` `grey` `light-violet` `violet` `blue` `light-blue` `yellow` `orange` `green` `light-green` `light-red` `red` `white`. No hex codes.
63
+
64
+ Keep all coordinates within bounds: gridX 0–50, gridY 0–40.
65
+
66
+ ### Example — a simple screen (8 elements)
67
+ ```json
68
+ {
69
+ "elements": [
70
+ {"type":"rectangle","gridX":0,"gridY":0,"gridWidth":50,"gridHeight":40,"fill":"white"},
71
+ {"type":"rectangle","gridX":0,"gridY":0,"gridWidth":50,"gridHeight":3,"fill":"light-violet"},
72
+ {"type":"headline","gridX":2,"gridY":1,"text":"Sign In","fontSize":18,"fill":"white"},
73
+ {"type":"input","gridX":15,"gridY":12,"gridWidth":20,"gridHeight":2,"text":"Email","fill":"white","stroke":"grey"},
74
+ {"type":"input","gridX":15,"gridY":16,"gridWidth":20,"gridHeight":2,"text":"Password","fill":"white","stroke":"grey"},
75
+ {"type":"button","gridX":15,"gridY":21,"gridWidth":20,"gridHeight":3,"text":"Sign In","fill":"blue"},
76
+ {"type":"text","gridX":18,"gridY":26,"text":"Forgot password?","fontSize":12,"fill":"blue"}
77
+ ]
78
+ }
79
+ ```
80
+
81
+ ---
82
+
83
+ ## ONE-TIME SETUP — run Steps 3 and 4 exactly once before the screen loop
84
+
85
+ ### Step 3 — Resolve or create the chapter
86
+
87
+ **If `chapterId` was provided in Step 1** — set `CHAPTER_ID = chapterId` and skip to Step 4. Do not make any API call here.
88
+
89
+ **If `chapterId` was NOT provided** — create a new chapter (exactly once):
90
+
91
+ ```bash
92
+ curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/chapters" \
93
+ -H "Content-Type: application/json" \
94
+ -d '{"position":{"x":0,"y":0}}'
95
+ ```
96
+
97
+ Extract `id` from the response → `CHAPTER_ID`.
98
+
99
+ **You now have exactly one `CHAPTER_ID`. Do not create another chapter.**
100
+
101
+ ### Step 4 — Fetch chapter state and build empty-column queue
102
+
103
+ ```bash
104
+ curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID"
105
+ ```
106
+
107
+ Parse `meta.timelineData` from the response:
108
+ - `rows` — list of row objects, each with `id` and `type`
109
+ - `columns` — list of column objects, each with `id`
110
+ - `cells` — list of cell objects, each with `rowId`, `colId`, and optionally `nodeId` (used only to check occupancy)
111
+
112
+ Find the row IDs for the `actor`, `interaction`, and `swimlane` row types. Save as `actorRowId`, `interactionRowId`, `swimlaneRowId`.
113
+
114
+ > **Cell ID convention**: Cell IDs are always `<rowId>-<columnId>`. Compute them directly — never search the `cells` array for an ID.
115
+
116
+ Build an **empty-column queue**: for each column (in order), compute `actorCellId = actorRowId + "-" + col.id`, `interactionCellId = interactionRowId + "-" + col.id`, `swimlaneCellId = swimlaneRowId + "-" + col.id`. Check these IDs in the `cells` array for a `nodeId` (absent or null). If ALL three have no `nodeId`, push `{actorCellId, interactionCellId, swimlaneCellId}` onto the queue.
117
+
118
+ ---
119
+
120
+ ## SCREEN LOOP — repeat Steps 5a–5d once per screen (N iterations total)
121
+
122
+ Process screens **one at a time**. Do not start the next screen until the current one is fully complete (node created + sketch rendered + verified).
123
+
124
+ **You have ONE chapter (`CHAPTER_ID`). All screens go into this same chapter. Do NOT call the chapter creation endpoint again inside this loop.**
125
+
126
+ Only SCREEN nodes are created. COMMAND and EVENT nodes are not created.
127
+
128
+ ### Step 5a — Acquire a column slot for this screen
129
+
130
+ **If the empty-column queue is non-empty** — pop the first entry. Use its `actorCellId` directly, proceed to Step 5b.
131
+
132
+ **If the empty-column queue is empty** — add a new column (this does NOT create a new chapter):
133
+
134
+ ```bash
135
+ curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/timelines/$CHAPTER_ID/columns" \
136
+ -H "Content-Type: application/json" \
137
+ -d '{}'
138
+ ```
139
+
140
+ Extract `columnId` from the response. Compute the actor cell ID directly:
141
+
142
+ **`actorCellId = actorRowId + "-" + columnId`**
143
+
144
+ (Cell IDs are always `<rowId>-<columnId>` — no re-fetch or cell array search needed.)
145
+
146
+ **In both cases**, generate a node UUID: `SCREEN_NODE_ID`. Generate an event UUID: `ACTOR_EVT_ID`.
147
+
148
+ Place the SCREEN node into the actor cell using the `/nodes/events` endpoint (agent-compatible — requires `x-user-id: agent`):
149
+
150
+ > **Do NOT use** `/boards/$BOARD_ID/events` — that endpoint requires a user JWT and will fail for agent (x-token) auth with "Authenticated user id is required".
151
+
152
+ ```bash
153
+ curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/events" \
154
+ -H "x-token: $TOKEN" \
155
+ -H "x-board-id: $BOARD_ID" \
156
+ -H "x-user-id: agent" \
157
+ -H "Content-Type: application/json" \
158
+ -d '[
159
+ {
160
+ "id": "<ACTOR_EVT_ID>",
161
+ "eventType": "node:created",
162
+ "nodeId": "<SCREEN_NODE_ID>",
163
+ "boardId": "<BOARD_ID>",
164
+ "timestamp": <NOW_MS>,
165
+ "chapterId": "<CHAPTER_ID>",
166
+ "cellId": "<actorCellId>",
167
+ "meta": {"type": "SCREEN", "title": "<screenTitle>", "description": "<visualDescription>"},
168
+ "node": {"id": "<SCREEN_NODE_ID>", "data": {}}
169
+ }
170
+ ]'
171
+ ```
172
+
173
+ Verify the response contains `"hashes"`. If it fails, stop and report the error — do not proceed to the sketch step.
174
+
175
+ > **Do NOT call the `drop` endpoint after this step.** `node:created` with `cellId` already places the node in the correct cell. Calling drop afterwards creates a duplicate cell reference without removing the original, causing the node to appear in two columns simultaneously.
176
+
177
+ ### Step 5b — Render the sketch onto the SCREEN node
178
+
179
+ ```bash
180
+ curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/images/$SCREEN_NODE_ID/sketch" \
181
+ -H "x-token: $TOKEN" \
182
+ -H "x-board-id: $BOARD_ID" \
183
+ -H "x-user-id: agent" \
184
+ -H "Content-Type: application/json" \
185
+ -d '{"description": "<screenTitle — what this screen shows>", "elements": [...]}'
186
+ ```
187
+
188
+ ### Step 5c — Verify the screen
189
+
190
+ Confirm the node and its rendered image both actually exist before moving on:
191
+
192
+ ```bash
193
+ curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/screens/$SCREEN_NODE_ID/verify" \
194
+ -H "x-token: $TOKEN"
195
+ ```
196
+
197
+ Check the `valid` field in the response:
198
+ - **`valid: true`** — proceed to Step 5d.
199
+ - **`valid: false`** — read the `error` field. If `nodeExists` is `false`, the node creation in Step 5a failed; retry it. If `imageExists` is `false`, the sketch render in Step 5b failed silently; retry Step 5b once. If it fails verification again, log the error for this screen in the final report and move on to the next screen — do not get stuck retrying indefinitely.
200
+
201
+ ### Step 5d — Mark the task complete
202
+
203
+ After the node, sketch, and verification all succeed, mark the task for this screen as completed using TaskUpdate.
204
+
205
+ ---
206
+
207
+ ## Step 6 — Report back
208
+
209
+ After all screens are done, summarise:
210
+ - Chapter ID
211
+ - Numbered list: screen title
212
+ - Any errors (with status codes), including screens that failed the Step 5c verification
@@ -0,0 +1,123 @@
1
+ ---
2
+ name: storyboard-screen
3
+ description: Design and render a single AI-generated wireframe screen onto an existing SCREEN node using the sketch API
4
+ ---
5
+
6
+ # Storyboard Screen Designer
7
+
8
+ > **Before doing anything else**, invoke the `connect` skill to resolve `TOKEN`, `BOARD_ID`, and `BASE_URL`. Do not proceed until the connect skill has completed.
9
+
10
+ > **MANDATORY RENDER + VERIFY**: The sketch API call in Step 4 and the verification in Step 5 are **not optional**. This skill exists solely to produce a rendered wireframe. A SCREEN node without a rendered sketch is an empty placeholder that adds no value to the model. If the sketch API call is skipped or fails, or verification reports `valid: false`, the task is incomplete — retry or report the error.
11
+
12
+ Design a single wireframe screen and render it onto an existing SCREEN node. Use this to redesign a screen, add detail to a placeholder, or update a screen after a flow changes.
13
+
14
+ ## Step 1 — Parse arguments
15
+
16
+ From `$ARGUMENTS`, extract:
17
+
18
+ | Field | How to find it | Default |
19
+ |-------|---------------|---------|
20
+ | `description` | what the screen should contain, e.g. "login with email and password" | required |
21
+ | `boardId` | a board ID string | from `connect` skill (`BOARD_ID`) |
22
+ | `nodeId` | the SCREEN node UUID to update | **ask the user if missing** |
23
+ | `baseUrl` | explicit URL override | from `connect` skill (`BASE_URL`) |
24
+
25
+ If `nodeId` is missing, ask for it before doing anything. `BOARD_ID` and `BASE_URL` come from the `connect` skill.
26
+
27
+ ## Step 2 — If updating an existing screen, load its current description first
28
+
29
+ If `nodeId` refers to a screen that has already been rendered (i.e. this is an adjustment/tweak, not a brand-new screen), **do not design from scratch**. First load the existing sketch description so the edit preserves the rest of the layout:
30
+
31
+ ```bash
32
+ curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/images/$NODE_ID/description" \
33
+ -H "x-token: $TOKEN" \
34
+ -H "x-board-id: $BOARD_ID" \
35
+ -H "x-user-id: agent"
36
+ ```
37
+
38
+ - `200` — returns the previously stored `{ elements: [...] }`. Use this as the base and apply only the requested change (e.g. edit one element's `text`/`fill`, add/remove a specific element) — leave everything else untouched.
39
+ - `404` — no description stored yet (e.g. an older screen rendered before this endpoint existed, or a placeholder node). Fall back to designing from scratch in Step 3.
40
+
41
+ Skip this step entirely when the node is brand-new (no prior render) — go straight to Step 3.
42
+
43
+ ## Step 3 — Design the screen
44
+
45
+ Design the screen using the grid description language — either from scratch (new screen, or Step 2 returned `404`) or by editing the elements loaded in Step 2. Think carefully about the layout — what elements does this screen need? Where should they go on the 50×40 grid?
46
+
47
+ Also compose a `visualDescription` — a prose description (2–4 sentences) of the screen's visual layout and content, written so that someone who cannot see the image can understand what is shown: what UI sections appear, what text/labels are visible, where buttons and inputs are placed, and the overall purpose of the screen.
48
+
49
+ ## Grid description language
50
+
51
+ Canvas: **50 × 40 grid units** (1000 × 800 px, 1 unit = 20 px).
52
+
53
+ Always start with a full white background:
54
+ ```json
55
+ {"type":"rectangle","gridX":0,"gridY":0,"gridWidth":50,"gridHeight":40,"fill":"white"}
56
+ ```
57
+
58
+ ### Element types
59
+
60
+ | type | required fields | optional fields |
61
+ |------|----------------|-----------------|
62
+ | `rectangle` | gridX, gridY, gridWidth, gridHeight | fill, stroke |
63
+ | `text` | gridX, gridY, text | fontSize (default 12), fill, gridWidth |
64
+ | `headline` | gridX, gridY, text | fontSize (default 20), fill, gridWidth |
65
+ | `button` | gridX, gridY, gridWidth, gridHeight, text | fill, stroke |
66
+ | `input` | gridX, gridY, gridWidth, gridHeight, text (placeholder) | fill, stroke |
67
+ | `image` | gridX, gridY, gridWidth, gridHeight | fill (placeholder color) |
68
+ | `line` | gridX, gridY, gridX2, gridY2 | stroke |
69
+ | `circle` | gridX, gridY, gridRadius | fill, stroke |
70
+
71
+ ### Colors
72
+ Limited to: `black` `grey` `light-violet` `violet` `blue` `light-blue` `yellow` `orange` `green` `light-green` `light-red` `red` `white`. No hex codes.
73
+
74
+ **Default palette — gray shades**: Unless instructed otherwise, use a grayscale palette. Prefer `white` for surfaces, `grey` for backgrounds, containers, borders, and secondary/placeholder text, and `black` for headings and primary text. Only introduce color when the user explicitly requests it.
75
+
76
+ Keep all coordinates within bounds: gridX 0–50, gridY 0–40.
77
+
78
+ ### Example
79
+ ```json
80
+ {
81
+ "elements": [
82
+ {"type":"rectangle","gridX":0,"gridY":0,"gridWidth":50,"gridHeight":40,"fill":"white"},
83
+ {"type":"rectangle","gridX":0,"gridY":0,"gridWidth":50,"gridHeight":3,"fill":"#424242"},
84
+ {"type":"headline","gridX":2,"gridY":1,"text":"Dashboard","fontSize":18,"fill":"white"},
85
+ {"type":"text","gridX":2,"gridY":6,"text":"Welcome back","fontSize":14,"fill":"grey"},
86
+ {"type":"rectangle","gridX":2,"gridY":9,"gridWidth":21,"gridHeight":8,"fill":"#e0e0e0","stroke":"#bdbdbd"},
87
+ {"type":"headline","gridX":4,"gridY":11,"text":"142","fontSize":24,"fill":"#424242"},
88
+ {"type":"text","gridX":4,"gridY":14,"text":"Orders this month","fontSize":11,"fill":"grey"},
89
+ {"type":"button","gridX":35,"gridY":36,"gridWidth":12,"gridHeight":2,"text":"Logout","fill":"#bdbdbd","stroke":"grey"}
90
+ ]
91
+ }
92
+ ```
93
+
94
+ ## Step 4 — Render the sketch
95
+
96
+ ```bash
97
+ curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/images/$NODE_ID/sketch" \
98
+ -H "x-token: $TOKEN" \
99
+ -H "x-board-id: $BOARD_ID" \
100
+ -H "x-user-id: agent" \
101
+ -H "Content-Type: application/json" \
102
+ -d '{"description": "<what this screen shows>", "elements": [...]}'
103
+ ```
104
+
105
+ Expect `204 No Content` on success.
106
+
107
+ ## Step 5 — Verify the screen
108
+
109
+ Confirm the node and its rendered image both actually exist:
110
+
111
+ ```bash
112
+ curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/screens/$NODE_ID/verify" \
113
+ -H "x-token: $TOKEN"
114
+ ```
115
+
116
+ If `valid` is `false`, read the `error` field and retry the failing step (Step 4 if `imageExists` is `false`) once before reporting failure.
117
+
118
+ ## Step 6 — Report back
119
+
120
+ Tell the user:
121
+ - The node ID that was updated
122
+ - Whether the render succeeded (HTTP 204) and verification passed (`valid: true`)
123
+ - Any errors