@eventmodelers/cli 2.0.1 → 2.0.3

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 (80) hide show
  1. package/README.md +27 -9
  2. package/cli.js +369 -36
  3. package/lib/build-kits.js +23 -0
  4. package/lib/scaffold-model.js +71 -0
  5. package/lib/scaffold.js +224 -0
  6. package/lib/supervise.js +60 -0
  7. package/lib/ui-baseline.js +123 -0
  8. package/package.json +1 -1
  9. package/shared/build-kit/lib/build-log-share.js +149 -0
  10. package/shared/build-kit/lib/events-view.js +18 -3
  11. package/shared/build-kit/lib/ralph.js +27 -16
  12. package/shared/build-kit/lib/slice-events.js +42 -2
  13. package/shared/build-kit/lib/slice-info.js +52 -0
  14. package/shared/build-kit/lib/slice-run.js +2 -2
  15. package/shared/build-kit/lib/slice-state.js +6 -4
  16. package/shared/scaffold/LICENSE-em-scaffold +21 -0
  17. package/shared/scaffold/supabase/generate.mjs +290 -0
  18. package/shared/scaffold/supabase/template/scripts/create-user.mjs.tmpl +29 -0
  19. package/shared/scaffold/supabase/template/scripts/dev.mjs.tmpl +145 -0
  20. package/shared/scaffold/supabase/template/scripts/write-env.mjs.tmpl +44 -0
  21. package/shared/scaffold/supabase/template/server.ts.tmpl +168 -0
  22. package/shared/scaffold/supabase/template/src/common/extraEndpoints.ts.tmpl +5 -0
  23. package/shared/scaffold/supabase/template/src/common/notImplemented.test.ts.tmpl +58 -0
  24. package/shared/scaffold/supabase/template/src/common/notImplemented.ts.tmpl +29 -0
  25. package/shared/scaffold/supabase/template/supabase/migrations/_V0__supabase_stubs.sql.tmpl +34 -0
  26. package/shared/scaffold/supabase/template/web/src/App.tsx.tmpl +38 -0
  27. package/shared/scaffold/supabase/template/web/src/app/Layout.tsx.tmpl +48 -0
  28. package/shared/scaffold/supabase/template/web/src/app/NotFound.tsx.tmpl +12 -0
  29. package/shared/scaffold/supabase/template/web/src/app/SignIn.tsx.tmpl +42 -0
  30. package/shared/scaffold/supabase/template/web/src/app/TimelinePage.tsx.tmpl +73 -0
  31. package/shared/scaffold/supabase/template/web/src/app/pages.ts.tmpl +26 -0
  32. package/shared/scaffold/supabase/template/web/src/app/skeleton.css.tmpl +25 -0
  33. package/shared/scaffold/supabase/template/web/src/lib/NotBuilt.tsx.tmpl +12 -0
  34. package/shared/scaffold/supabase/template/web/src/lib/api.ts.tmpl +226 -0
  35. package/shared/scaffold/supabase/template/web/src/main.tsx.tmpl +13 -0
  36. package/shared/skills/learn-eventmodelers-api/SKILL.md +4 -4
  37. package/shared/skills/ui-contract/SKILL.md +6 -6
  38. package/shared/skills/update-slice-status/SKILL.md +1 -1
  39. package/stacks/axon/templates/build-kit/CLAUDE.md +4 -3
  40. package/stacks/axon/templates/build-kit/lib/backend-prompt.md +11 -1
  41. package/stacks/axon/templates/build-kit/lib/prompt.md +24 -0
  42. package/stacks/blank/templates/build-kit/CLAUDE.md +4 -3
  43. package/stacks/blank/templates/build-kit/lib/backend-prompt.md +11 -1
  44. package/stacks/blank/templates/build-kit/lib/prompt.md +24 -0
  45. package/stacks/classic/templates/build-kit/CLAUDE.md +6 -5
  46. package/stacks/classic/templates/build-kit/lib/backend-prompt.md +11 -1
  47. package/stacks/classic/templates/build-kit/lib/prompt.md +24 -0
  48. package/stacks/kurrent/templates/build-kit/CLAUDE.md +4 -3
  49. package/stacks/kurrent/templates/build-kit/lib/backend-prompt.md +11 -1
  50. package/stacks/kurrent/templates/build-kit/lib/prompt.md +24 -0
  51. package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/SKILL.md +1 -1
  52. package/stacks/modeling-kit/templates/.claude/skills/prototype/SKILL.md +2 -0
  53. package/stacks/modeling-kit/templates/.claude/skills/prototype/assets/prototype.mjs +7 -5
  54. package/stacks/modeling-kit/templates/kit/CLAUDE-STANDALONE.md +1 -1
  55. package/stacks/modeling-kit/templates/kit/CLAUDE.md +11 -5
  56. package/stacks/node/templates/build-kit/CLAUDE.md +4 -3
  57. package/stacks/node/templates/build-kit/lib/backend-prompt.md +11 -1
  58. package/stacks/node/templates/build-kit/lib/prompt.md +10 -0
  59. package/stacks/opencqrs/templates/build-kit/CLAUDE.md +4 -3
  60. package/stacks/opencqrs/templates/build-kit/lib/backend-prompt.md +11 -1
  61. package/stacks/opencqrs/templates/build-kit/lib/prompt.md +24 -0
  62. package/stacks/react/templates/build-kit/CLAUDE.md +4 -3
  63. package/stacks/react/templates/build-kit/lib/AGENT.md +1 -1
  64. package/stacks/react/templates/build-kit/lib/backend-prompt.md +17 -7
  65. package/stacks/react/templates/build-kit/lib/kit.json +1 -1
  66. package/stacks/react/templates/build-kit/lib/prompt.md +39 -2
  67. package/stacks/supabase/templates/.claude/skills/build-state-view/SKILL.md +15 -0
  68. package/stacks/supabase/templates/build-kit/CLAUDE.md +4 -3
  69. package/stacks/supabase/templates/build-kit/lib/backend-prompt.md +11 -1
  70. package/stacks/supabase/templates/build-kit/lib/prompt.md +10 -0
  71. package/stacks/supabase-react/templates/.claude/skills/build-state-change/SKILL.md +78 -6
  72. package/stacks/supabase-react/templates/.claude/skills/build-state-view/SKILL.md +64 -11
  73. package/stacks/supabase-react/templates/build-kit/CLAUDE.md +5 -4
  74. package/stacks/supabase-react/templates/build-kit/lib/AGENT.md +1 -1
  75. package/stacks/supabase-react/templates/build-kit/lib/backend-prompt.md +17 -7
  76. package/stacks/supabase-react/templates/build-kit/lib/kit.json +1 -1
  77. package/stacks/supabase-react/templates/build-kit/lib/prompt.md +40 -3
  78. package/stacks/umadb/templates/build-kit/CLAUDE.md +4 -3
  79. package/stacks/umadb/templates/build-kit/lib/backend-prompt.md +11 -1
  80. package/stacks/umadb/templates/build-kit/lib/prompt.md +24 -0
package/README.md CHANGED
@@ -137,23 +137,41 @@ Use skills in Claude Code with `/skill-name`:
137
137
 
138
138
  Which skills install depends on the chosen stack — see `stacks/<name>/templates/.claude/skills/`. `/connect`, `/learn-eventmodelers-api`, `/update-slice-status`, and `/request-feedback` have no stack-specific content and install into every stack from `shared/skills/` instead.
139
139
 
140
+ ## `scaffold`: a runnable app from your board
141
+
142
+ `scaffold <dir>` reads the board and generates an app skeleton around the build kits, so slice building starts from an app that already runs. `--type` picks what gets installed and generated:
143
+
144
+ | Type | Installs | You get |
145
+ |---|---|---|
146
+ | `supabase` (default) | `supabase` at the root, `supabase-react` in `web/` | Supabase email/password sign-in, a layout with one navigation entry per timeline, a page shell per screen at `web/src/pages/<ScreenTitle>.tsx`, `501 Not Implemented` for every modeled endpoint without a slice (a built route always wins), a "not built yet" placeholder in the UI, hot reload for both halves, `npm run up` |
147
+
148
+ Needs on the machine: Node 22, Docker, Supabase CLI, Flyway + Java (the command lists what is missing). The skeleton itself needs nothing beyond Node, on macOS, Linux and Windows: `npm run up` / `down` / `db:stop` / `user -- <email> <password>` / `check` all go through one plain script, `scripts/dev.mjs`. `<dir>` must be empty or new, or an earlier skeleton, which is refreshed from the current model — generated files are rewritten, pages and anything a builder wrote are kept. Titles come from the board verbatim; a screen's route comes only from a `route /orders` line in its description, the post-sign-in page from a "home page" line, and Bulma is used when the screen mockups use it. `--no-install` stops after generating, `--no-check` skips the checks; ports are picked free automatically and a refresh keeps them (`--port api=3200` pins one; repeatable).
149
+
150
+ **Your own templates.** `npx @eventmodelers/cli init-templates [paths...]` copies the built-in templates into `./templates/` for you to edit (`--list` shows the paths; name a file such as `web/src/app/Layout.tsx` or a folder such as `web/src/app` to copy only those, and take only what you mean to change — a copied template stops following the CLI's updates; existing files are kept unless `--force`). `scaffold` uses what it finds there and the built-in template for everything else. Or put `*.tmpl` files in a `templates/` folder in the app's root (or point `--templates <dir>` at one, which also works on the first run). A file there replaces the built-in template at the same path — `templates/web/src/app/Layout.tsx.tmpl` replaces `web/src/app/Layout.tsx` — and a path with no built-in counterpart is simply added. The same tokens apply (`__APP_TITLE__`, `__API_PORT__`, `__WEB_PORT__`, `__DB_PORT__`), and every refresh uses them again, so customisations survive a model change. Only `*.tmpl` files count, so nothing in `templates/` is compiled as project code. Files the scaffold writes once (pages, `extraEndpoints.ts`) are never overwritten whatever you put there; edit those in place.
151
+
152
+ The `supabase` skeleton generator is adapted from the em-scaffold skill by Adrian Mowat (MIT, `shared/scaffold/LICENSE-em-scaffold`).
153
+
140
154
  ## Everyday commands
141
155
 
142
156
  ```bash
143
157
  npx @eventmodelers/cli init --stack <name> # scaffold a stack + install + configure (alias: install)
144
158
  npx @eventmodelers/cli init --stack <name> --demo # same, plus a ready-made demo model in the kit's .slices/ to build against
159
+ npx @eventmodelers/cli scaffold <dir> --type supabase # app skeleton from the board (sign-in, navigation, a page per screen, 501 for unbuilt slices) + both kits installed, ready for `run`
145
160
  npx @eventmodelers/cli re-init # refresh an already-installed kit's scripts/skills only — never touches the root scaffold
146
161
  npx @eventmodelers/cli run # start the agent loop (ralph-claude.js) from the installed kit dir
162
+ npx @eventmodelers/cli run --all # every build kit in the project at once (this directory and up to two levels below, e.g. a scaffolded app's root and web/), prefixed output, Ctrl+c stops all; other flags (--agent, --handover, …) go to each
163
+ npx @eventmodelers/cli run --all --modeling # the same, plus a modeling agent running alongside the build kits
147
164
  npx @eventmodelers/cli run --local-ai [target] # board work only via a local/self-hosted model (ralph-local-ai.js) — does NOT build slices; build locally with --exec
148
165
  npx @eventmodelers/cli run --agent <name> # pick the agent: claude (default), opencode, codex, gemini — bare --agent lists them with their exact commands
149
166
  npx @eventmodelers/cli run --agent <name> --model <id> # …on a given model: -m for opencode/codex/gemini, --model for Claude, LOCAL_AI_MODEL for --local-ai
150
167
  npx @eventmodelers/cli run --exec "<command>" # manual fallback for any other harness (ralph-exec.js) — agent tracing needs its JSON flag, e.g. "codex exec --json --full-auto"
151
168
  npx @eventmodelers/cli run --local # skip platform config/credential lookup entirely — local-only, no board sync
169
+ npx @eventmodelers/cli run --sync # share this build agent's event log with the board, constantly, for a live dashboard (see agent-build-log.md)
152
170
  npx @eventmodelers/cli run --modeling # modeling-kit: warm Claude process driven by the board's prompt queue
153
171
  npx @eventmodelers/cli run --standalone # same, plus acting on board changes unprompted — and needs no install at all
154
172
  npx @eventmodelers/cli run --standalone --board-id <uuid> # …from any directory, against any board (see Power users)
155
173
  npx @eventmodelers/cli run --id <id> --name <label> # override this run's agent identity (id + display name); `init --name` persists a name instead
156
- npx @eventmodelers/cli run --handover # backend kit, this run only: finish slices that have a UI at `Handover` instead of `Done` (see Backend → UI handover)
174
+ npx @eventmodelers/cli run --handover # backend kit, this run only: finish slices that have a UI at `Waiting` instead of `Done` (see Backend → UI handover)
157
175
  npx @eventmodelers/cli config --handover on|off # persist that choice for the project (`handover` in .eventmodelers/config.json)
158
176
  npx @eventmodelers/cli fetch --context <name> # pull full slice detail for one context on the board into <kit-dir>/.slices/
159
177
  npx @eventmodelers/cli fetch --context <name> --slice-id <id> # same, then print just that slice
@@ -198,7 +216,7 @@ npx @eventmodelers/cli run --local
198
216
 
199
217
  and the agent starts building — `--local` skips platform config and credential lookup entirely, so it works straight out of `init` with nothing connected. Every TODO you fill in gets exercised on the next iteration against real slice data covering all four slice types (state change, state view, automation, translation), which is exactly the coverage a new stack's `build-*` skills need before it's worth promoting.
200
218
 
201
- **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.
219
+ **Backend → UI handover.** Besides the usual statuses a slice can be `Ready` (modeling done, waiting to be planned) and `Waiting` (backend built, UI contract published, waiting for the UI kit). The flow is `Ready → Planned → InProgress → Waiting → 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 `Waiting` 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": "Waiting" }` 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` / `Waiting` statuses.
202
220
 
203
221
  **UI contract (handover only).** With handover on, the scaffolded `build-state-view` / `build-state-change` skills 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/`).
204
222
 
@@ -652,7 +670,7 @@ Pair it with `--id`: a `--standalone`/`--global` run mints a fresh id per run, s
652
670
 
653
671
  ### Backend → UI handover (`--handover`)
654
672
 
655
- A slice with a UI is only finished once two kits have each built their half: the backend kit builds the endpoint and publishes its contract, the UI kit builds the frontend against it. `--handover` makes the first kit stop at `Handover` instead of `Done`, so the second one can claim the slice.
673
+ A slice with a UI is only finished once two kits have each built their half: the backend kit builds the endpoint and publishes its contract, the UI kit builds the frontend against it. `--handover` makes the first kit stop at `Waiting` instead of `Done`, so the second one can claim the slice.
656
674
 
657
675
  ```bash
658
676
  npx @eventmodelers/cli init --stack node --handover # persisted: "handover": true in .eventmodelers/config.json (also re-init)
@@ -663,18 +681,18 @@ npx @eventmodelers/cli run --handover # this run only — writes
663
681
 
664
682
  | | Handover off (default) | Handover on |
665
683
  |---|---|---|
666
- | Backend kit, STATE_VIEW / STATE_CHANGE slice | ends at `Done`, no UI contract published | publishes the UI contract, ends at `Handover` |
684
+ | Backend kit, STATE_VIEW / STATE_CHANGE slice | ends at `Done`, no UI contract published | publishes the UI contract, ends at `Waiting` |
667
685
  | Backend kit, automation / translation | `Done` | `Done` — no UI counterpart |
668
- | UI kit (`react`, `supabase-react`) | claims `Handover` only | claims `Handover` only, sets `Done` when its UI is built |
686
+ | UI kit (`react`, `supabase-react`) | claims `Waiting` only | claims `Waiting` only, sets `Done` when its UI is built |
669
687
 
670
- Flow: `Ready → Planned → InProgress → Handover → InProgress → Done`. Precedence for a run is `run --handover` over the project's `handover` setting; the runner states the effective value to the agent as a `Handover: on|off` line in its task, so the agent never has to work it out from `config.json`. `config --handover` writes to the project's config (the installed kit's project, else the nearest `.eventmodelers/config.json` up the tree — never the account-wide one in `~/`).
688
+ Flow: `Ready → Planned → InProgress → Waiting → InProgress → Done`. Precedence for a run is `run --handover` over the project's `handover` setting; the runner states the effective value to the agent as a `Handover: on|off` line in its task, so the agent never has to work it out from `config.json`. `config --handover` writes to the project's config (the installed kit's project, else the nearest `.eventmodelers/config.json` up the tree — never the account-wide one in `~/`).
671
689
 
672
690
  **What to know before relying on it**
673
691
 
674
- - **UI kits only ever claim `Handover`.** Against a board whose backend finished slices at `Done`, a `react` / `supabase-react` kit has nothing to do and replies `NO_TASKS` — the backend side must run with handover on.
692
+ - **UI kits only ever claim `Waiting`.** Against a board whose backend finished slices at `Done`, a `react` / `supabase-react` kit has nothing to do and replies `NO_TASKS` — the backend side must run with handover on.
675
693
  - **Both sides need the new kit.** An already-installed backend kit keeps its old `ralph.js` and prompts and silently ignores `--handover` (slices still end at `Done`); `re-init` it to pick the feature up.
676
- - **The platform must know the `Ready` / `Handover` statuses.**
677
- - A kit declares what it claims in `lib/kit.json` — `{ "pickupStatus": "Handover" }`; absent means `Planned`.
694
+ - **The platform must know the `Ready` / `Waiting` statuses.**
695
+ - A kit declares what it claims in `lib/kit.json` — `{ "pickupStatus": "Waiting" }`; absent means `Planned`.
678
696
  - `run --status-trigger <status>` overrides that pickup status for a single run (e.g. `--status-trigger Ready`); nothing is saved.
679
697
 
680
698
  The contract itself is the shared `ui-contract` skill's job (see above); it also owns the rule for which status a slice ends at.