@eventmodelers/cli 1.0.163 → 2.0.2

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 (128) hide show
  1. package/README.md +48 -36
  2. package/RELEASE_NOTES.md +20 -2
  3. package/cli.js +774 -247
  4. package/lib/adapters/spec-kitty-adapter.js +3 -7
  5. package/lib/build-kits.js +23 -0
  6. package/lib/global-agent-id.js +45 -0
  7. package/lib/modeling-local-ai.js +4 -2
  8. package/lib/scaffold-model.js +71 -0
  9. package/lib/scaffold.js +224 -0
  10. package/lib/supervise.js +60 -0
  11. package/lib/ui-baseline.js +123 -0
  12. package/package.json +1 -1
  13. package/shared/build-kit/README.md +4 -19
  14. package/shared/build-kit/lib/agent-lock.js +75 -0
  15. package/shared/build-kit/lib/agent-name.js +27 -0
  16. package/shared/build-kit/lib/event-prune.js +58 -0
  17. package/shared/build-kit/lib/events-view.js +134 -0
  18. package/shared/build-kit/lib/local-ai-agent.js +14 -26
  19. package/shared/build-kit/lib/mcp-register.js +17 -1
  20. package/shared/build-kit/lib/prompt-journal.js +191 -0
  21. package/shared/build-kit/lib/prompt-replay.js +35 -0
  22. package/shared/build-kit/lib/ralph.js +218 -169
  23. package/shared/build-kit/lib/slice-curation.js +40 -0
  24. package/shared/build-kit/lib/slice-events.js +93 -0
  25. package/shared/build-kit/lib/slice-git.js +86 -0
  26. package/shared/build-kit/lib/slice-learnings.js +197 -0
  27. package/shared/build-kit/lib/slice-projection.js +101 -0
  28. package/shared/build-kit/lib/slice-run.js +181 -0
  29. package/shared/build-kit/lib/slice-state.js +184 -0
  30. package/shared/build-kit/lib/slice-tasks.js +35 -0
  31. package/shared/build-kit/ralph-claude.js +4 -2
  32. package/shared/build-kit/ralph-exec.js +4 -2
  33. package/shared/build-kit/ralph-local-ai.js +5 -3
  34. package/shared/scaffold/LICENSE-em-scaffold +21 -0
  35. package/shared/scaffold/supabase/generate.mjs +290 -0
  36. package/shared/scaffold/supabase/template/scripts/create-user.mjs.tmpl +29 -0
  37. package/shared/scaffold/supabase/template/scripts/dev.mjs.tmpl +145 -0
  38. package/shared/scaffold/supabase/template/scripts/write-env.mjs.tmpl +44 -0
  39. package/shared/scaffold/supabase/template/server.ts.tmpl +168 -0
  40. package/shared/scaffold/supabase/template/src/common/extraEndpoints.ts.tmpl +5 -0
  41. package/shared/scaffold/supabase/template/src/common/notImplemented.test.ts.tmpl +58 -0
  42. package/shared/scaffold/supabase/template/src/common/notImplemented.ts.tmpl +29 -0
  43. package/shared/scaffold/supabase/template/supabase/migrations/_V0__supabase_stubs.sql.tmpl +34 -0
  44. package/shared/scaffold/supabase/template/web/src/App.tsx.tmpl +38 -0
  45. package/shared/scaffold/supabase/template/web/src/app/Layout.tsx.tmpl +48 -0
  46. package/shared/scaffold/supabase/template/web/src/app/NotFound.tsx.tmpl +12 -0
  47. package/shared/scaffold/supabase/template/web/src/app/SignIn.tsx.tmpl +42 -0
  48. package/shared/scaffold/supabase/template/web/src/app/TimelinePage.tsx.tmpl +73 -0
  49. package/shared/scaffold/supabase/template/web/src/app/pages.ts.tmpl +26 -0
  50. package/shared/scaffold/supabase/template/web/src/app/skeleton.css.tmpl +25 -0
  51. package/shared/scaffold/supabase/template/web/src/lib/NotBuilt.tsx.tmpl +12 -0
  52. package/shared/scaffold/supabase/template/web/src/lib/api.ts.tmpl +226 -0
  53. package/shared/scaffold/supabase/template/web/src/main.tsx.tmpl +13 -0
  54. package/shared/skills/learn-eventmodelers-api/SKILL.md +17 -4
  55. package/shared/skills/request-feedback/SKILL.md +1 -2
  56. package/shared/skills/ui-contract/SKILL.md +7 -5
  57. package/shared/skills/update-slice-status/SKILL.md +1 -1
  58. package/stacks/axon/templates/build-kit/CLAUDE.md +7 -6
  59. package/stacks/axon/templates/build-kit/lib/AGENT.md +6 -7
  60. package/stacks/axon/templates/build-kit/lib/backend-prompt.md +20 -42
  61. package/stacks/axon/templates/build-kit/lib/prompt.md +32 -41
  62. package/stacks/blank/templates/build-kit/CLAUDE.md +7 -6
  63. package/stacks/blank/templates/build-kit/lib/AGENT.md +6 -7
  64. package/stacks/blank/templates/build-kit/lib/backend-prompt.md +20 -37
  65. package/stacks/blank/templates/build-kit/lib/prompt.md +33 -42
  66. package/stacks/classic/templates/.claude/skills/build-automation/SKILL.md +1 -1
  67. package/stacks/classic/templates/.claude/skills/build-state-change/SKILL.md +1 -1
  68. package/stacks/classic/templates/.claude/skills/build-state-view/SKILL.md +2 -2
  69. package/stacks/classic/templates/.claude/skills/record-evidence/SKILL.md +2 -2
  70. package/stacks/classic/templates/build-kit/CLAUDE.md +9 -8
  71. package/stacks/classic/templates/build-kit/lib/AGENT.md +6 -7
  72. package/stacks/classic/templates/build-kit/lib/backend-prompt.md +20 -38
  73. package/stacks/classic/templates/build-kit/lib/prompt.md +33 -42
  74. package/stacks/kurrent/templates/build-kit/CLAUDE.md +7 -7
  75. package/stacks/kurrent/templates/build-kit/lib/AGENT.md +6 -7
  76. package/stacks/kurrent/templates/build-kit/lib/backend-prompt.md +20 -42
  77. package/stacks/kurrent/templates/build-kit/lib/prompt.md +32 -41
  78. package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/SKILL.md +1 -1
  79. package/stacks/modeling-kit/templates/.claude/skills/detect-model-drift/SKILL.md +1 -1
  80. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-core-rules/SKILL.md +2 -0
  81. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +7 -3
  82. package/stacks/modeling-kit/templates/.claude/skills/wdyt/SKILL.md +1 -1
  83. package/stacks/modeling-kit/templates/kit/CLAUDE-STANDALONE.md +3 -4
  84. package/stacks/modeling-kit/templates/kit/CLAUDE.md +18 -28
  85. package/stacks/modeling-kit/templates/kit/README.md +1 -1
  86. package/stacks/modeling-kit/templates/kit/lib/config.js +1 -1
  87. package/stacks/node/templates/build-kit/CLAUDE.md +7 -6
  88. package/stacks/node/templates/build-kit/lib/AGENT.md +6 -7
  89. package/stacks/node/templates/build-kit/lib/backend-prompt.md +20 -42
  90. package/stacks/node/templates/build-kit/lib/prompt.md +18 -41
  91. package/stacks/opencqrs/templates/build-kit/CLAUDE.md +7 -7
  92. package/stacks/opencqrs/templates/build-kit/lib/AGENT.md +6 -7
  93. package/stacks/opencqrs/templates/build-kit/lib/backend-prompt.md +20 -42
  94. package/stacks/opencqrs/templates/build-kit/lib/prompt.md +32 -41
  95. package/stacks/react/templates/.claude/skills/build-state-change/SKILL.md +1 -1
  96. package/stacks/react/templates/.claude/skills/build-state-view/SKILL.md +1 -1
  97. package/stacks/react/templates/build-kit/CLAUDE.md +7 -6
  98. package/stacks/react/templates/build-kit/lib/AGENT.md +6 -7
  99. package/stacks/react/templates/build-kit/lib/backend-prompt.md +25 -42
  100. package/stacks/react/templates/build-kit/lib/kit.json +1 -1
  101. package/stacks/react/templates/build-kit/lib/prompt.md +47 -43
  102. package/stacks/supabase/templates/.claude/skills/build-state-view/SKILL.md +15 -0
  103. package/stacks/supabase/templates/build-kit/CLAUDE.md +7 -6
  104. package/stacks/supabase/templates/build-kit/lib/AGENT.md +6 -7
  105. package/stacks/supabase/templates/build-kit/lib/backend-prompt.md +20 -42
  106. package/stacks/supabase/templates/build-kit/lib/prompt.md +18 -41
  107. package/stacks/supabase-react/templates/.claude/skills/build-state-change/SKILL.md +80 -8
  108. package/stacks/supabase-react/templates/.claude/skills/build-state-view/SKILL.md +66 -13
  109. package/stacks/supabase-react/templates/build-kit/CLAUDE.md +8 -7
  110. package/stacks/supabase-react/templates/build-kit/lib/AGENT.md +6 -7
  111. package/stacks/supabase-react/templates/build-kit/lib/backend-prompt.md +25 -42
  112. package/stacks/supabase-react/templates/build-kit/lib/kit.json +1 -1
  113. package/stacks/supabase-react/templates/build-kit/lib/prompt.md +48 -44
  114. package/stacks/umadb/templates/build-kit/CLAUDE.md +7 -6
  115. package/stacks/umadb/templates/build-kit/lib/AGENT.md +6 -7
  116. package/stacks/umadb/templates/build-kit/lib/backend-prompt.md +20 -42
  117. package/stacks/umadb/templates/build-kit/lib/prompt.md +32 -41
  118. package/shared/bridge/spec-kitty/bridge-spec-kitty-specify/SKILL.md +0 -125
  119. package/shared/bridge/spec-kitty/bridge-spec-kitty-tasks/SKILL.md +0 -95
  120. package/shared/build-kit/lib/agent.sh +0 -20
  121. package/shared/build-kit/ralph.sh +0 -111
  122. package/shared/build-kit/realtime-agent.js +0 -20
  123. package/stacks/bridge/templates/bridge/lib/AGENT.md +0 -47
  124. package/stacks/bridge/templates/bridge/lib/prompt.md +0 -111
  125. package/stacks/bridge/templates/bridge/ralph-claude.js +0 -50
  126. package/stacks/bridge/templates/bridge/ralph-hook.js +0 -103
  127. package/stacks/bridge/templates/bridge/ralph-local-ai.js +0 -43
  128. package/stacks/bridge/templates/bridge/ralph-static.js +0 -90
package/README.md CHANGED
@@ -92,7 +92,6 @@ your-project/
92
92
  │ ├── ralph-claude.js ← realtime agent + task loop
93
93
  │ ├── ralph-local-ai.js ← same, via a local/self-hosted model
94
94
  │ ├── ralph-exec.js ← same, via an external agent command (Codex CLI, OpenCode, …)
95
- │ ├── ralph.sh ← bash-only loop (no realtime)
96
95
  │ ├── lib/ ← stack-specific agent prompts + helpers
97
96
  │ └── .slices/ ← board slices, written by `fetch`/`listen` (or pre-seeded by `init --demo`)
98
97
  ├── .claude/
@@ -138,24 +137,40 @@ Use skills in Claude Code with `/skill-name`:
138
137
 
139
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.
140
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
+
141
154
  ## Everyday commands
142
155
 
143
156
  ```bash
144
157
  npx @eventmodelers/cli init --stack <name> # scaffold a stack + install + configure (alias: install)
145
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`
146
160
  npx @eventmodelers/cli re-init # refresh an already-installed kit's scripts/skills only — never touches the root scaffold
147
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
148
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
149
165
  npx @eventmodelers/cli run --agent <name> # pick the agent: claude (default), opencode, codex, gemini — bare --agent lists them with their exact commands
150
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
151
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"
152
- npx @eventmodelers/cli run --bash # bash-only loop, no realtime (ralph.sh)
153
168
  npx @eventmodelers/cli run --local # skip platform config/credential lookup entirely — local-only, no board sync
154
169
  npx @eventmodelers/cli run --modeling # modeling-kit: warm Claude process driven by the board's prompt queue
155
170
  npx @eventmodelers/cli run --standalone # same, plus acting on board changes unprompted — and needs no install at all
156
171
  npx @eventmodelers/cli run --standalone --board-id <uuid> # …from any directory, against any board (see Power users)
157
172
  npx @eventmodelers/cli run --id <id> --name <label> # override this run's agent identity (id + display name); `init --name` persists a name instead
158
- 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)
173
+ 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)
159
174
  npx @eventmodelers/cli config --handover on|off # persist that choice for the project (`handover` in .eventmodelers/config.json)
160
175
  npx @eventmodelers/cli fetch --context <name> # pull full slice detail for one context on the board into <kit-dir>/.slices/
161
176
  npx @eventmodelers/cli fetch --context <name> --slice-id <id> # same, then print just that slice
@@ -200,7 +215,7 @@ npx @eventmodelers/cli run --local
200
215
 
201
216
  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.
202
217
 
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.
218
+ **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.
204
219
 
205
220
  **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/`).
206
221
 
@@ -210,7 +225,7 @@ Installing both a build stack and `init-modeling` into the same project reuses t
210
225
 
211
226
  A modeling-kit install has one runtime: a warm Claude process the CLI keeps alive across
212
227
  turns and feeds directly over stdin, so a prompt typed (or spoken) on the board is picked up
213
- with no cold start and no `tasks.json` round trip.
228
+ with no cold start and no task-queue round trip.
214
229
 
215
230
  ```bash
216
231
  npx @eventmodelers/cli run --modeling # react to prompts sent to this board
@@ -273,7 +288,7 @@ pass `--model <id>` as usual.
273
288
 
274
289
  Claude and Gemini need a gateway (e.g. LiteLLM) in front of a non-native server, since they only speak their own protocol.
275
290
 
276
- `--agent`, `--exec`, `--local-ai` and `--bash` each pick the runner, so pass at most one of them —
291
+ `--agent`, `--exec` and `--local-ai` each pick the runner, so pass at most one of them —
277
292
  any two together are rejected. For a local or self-hosted model use `--local-ai`, not `--agent`.
278
293
 
279
294
  To drive either one with a local model instead of Claude, see [Running the modeling agent on a local model](#running-the-modeling-agent-on-a-local-model).
@@ -524,40 +539,37 @@ npx @eventmodelers/cli init-modeling --global
524
539
 
525
540
  Everything else (the kit dir, project scaffold, credentials, MCP registration) still targets the current directory as usual — `--global` only changes where skills land.
526
541
 
527
- ### Bridging to another spec framework
542
+ ### Syncing a context to Spec Kitty
528
543
 
529
- If you drive development with a different spec/task framework (Spec Kitty today; more later) instead of build-kit's own code generation, a **bridge** kit keeps that framework's artifacts in sync with the board instead of writing application code:
544
+ If you drive development with Spec Kitty instead of build-kit's own code generation, `fetch` can restate a context for it in one shot:
530
545
 
531
546
  ```bash
532
- npx @eventmodelers/cli init --bridge --target spec-kitty
533
- npx @eventmodelers/cli bridge
547
+ npx @eventmodelers/cli fetch --context Ticketing --spec-kitty
534
548
  ```
535
549
 
536
- `init --bridge` installs a `.bridge-kit/` (mirrors `.build-kit/`'s realtime + task-queue loop) plus only the skills for the chosen `--target` (`shared/bridge/<target>/`) — a `spec-kitty` bridge never installs Kiro's skills, and vice versa. `bridge` starts the loop: on every board slice change (not just "Planned", unlike build-kit), it regenerates that framework's spec artifacts from the current board state. It doesn't build code and doesn't claim slices.
550
+ That sync is deterministic and stops well short of writing Spec Kitty's own artifacts — `lib/adapters/spec-kitty-adapter.js` fetches full slice detail and restates it as a plain markdown mission brief (one section per slice, its scenarios verbatim, nothing invented), then calls `spec-kitty intake --force` to install it at `.kittify/mission-brief.md`. It deliberately doesn't create the mission, write `spec.md`, or author work packages — Spec Kitty's own `/spec-kitty.specify` → `/spec-kitty.plan` → `/spec-kitty.tasks` pipeline does that, because those steps need real judgment (work package boundaries, which files a WP owns, which agent profile fits) that only makes sense with actual codebase context, which this adapter doesn't have. What it replaces is Spec Kitty's *interactive discovery interview*: `/spec-kitty.specify`'s own "Brief Context Detection" step reads `.kittify/mission-brief.md` when present and extracts requirements from it instead of asking the user, so the event model — not a live Q&A — becomes the input. No LLM call happens in this adapter.
537
551
 
538
- For `spec-kitty`, that sync is deterministic and stops well short of writing Spec Kitty's own artifacts — `lib/adapters/spec-kitty-adapter.js` fetches full slice detail and restates it as a plain markdown mission brief (one section per slice, its scenarios verbatim, nothing invented), then calls `spec-kitty intake --force` to install it at `.kittify/mission-brief.md`. It deliberately doesn't create the mission, write `spec.md`, or author work packages — Spec Kitty's own `/spec-kitty.specify` → `/spec-kitty.plan` → `/spec-kitty.tasks` pipeline does that, because those steps need real judgment (work package boundaries, which files a WP owns, which agent profile fits) that only makes sense with actual codebase context, which this adapter doesn't have. What it replaces is Spec Kitty's *interactive discovery interview*: `/spec-kitty.specify`'s own "Brief Context Detection" step reads `.kittify/mission-brief.md` when present and extracts requirements from it instead of asking the user, so the event model — not a live Q&A — becomes the input. No LLM call happens in this adapter's own path, and `bridge` picks it automatically whenever a target has one (`--claude` forces the Claude runner instead). Targets without a static adapter yet fall back to Claude re-running `bridge-<target>-specify`; pass `--local-ai` for the local-model runner instead (same caveat as build-kit's `--local-ai`: `lib/local-ai-agent.js` is shared as-is).
552
+ `spec-kitty init` (Spec Kitty's own project setup) has to have already been run in the project root — the adapter checks for `.kittify/` first and stops with the exact command to run if it's missing, rather than failing deep inside a cryptic `spec-kitty` CLI error. After the sync, run `/spec-kitty.specify` in your coding agent to turn the brief into an actual mission (the plain `spec-kitty specify` CLI command only scaffolds — brief detection is in the agent-driven prompt).
539
553
 
540
- Don't want the standing loop at all? `fetch` can call the same adapter for a single one-shot sync, no `.bridge-kit/` install required:
554
+ (The standing bridge loop — `init --bridge` / `bridge` — is retired for now.)
541
555
 
542
- ```bash
543
- npx @eventmodelers/cli fetch --context Ticketing --spec-kitty
544
- ```
545
-
546
- Either way, `spec-kitty init` (Spec Kitty's own project setup) has to have already been run in the project root — the adapter checks for `.kittify/` first and stops with the exact command to run if it's missing, rather than failing deep inside a cryptic `spec-kitty` CLI error. After the sync, run `/spec-kitty.specify` in your coding agent to turn the brief into an actual mission (the plain `spec-kitty specify` CLI command only scaffolds — brief detection is in the agent-driven prompt).
556
+ ### Reading the event log
547
557
 
548
- ### Overriding the executor with a hook
549
-
550
- Claude is only the default — some teams don't want an AI agent in this loop at all, e.g. they'd rather just commit + push the board export and let a CI pipeline own the actual translation. `--hook` replaces the AI executor with an arbitrary shell command, run once per batch of slice changes:
558
+ Everything an agent does to a slice, a task, a prompt or a learning is an event in the kit's log (`.build-kit/events/` and `events.log`, or `.agent-modeling-kit/` for a modeling agent). `eventmodelers events` decodes it in the terminal — one line per event, with the slice's title and the first four characters of the agent that wrote it:
551
559
 
552
560
  ```bash
553
- npx @eventmodelers/cli init --bridge --target spec-kitty --hook "git add .slices && git commit -m sync && git push"
554
- npx @eventmodelers/cli bridge
561
+ npx @eventmodelers/cli events # the build kit of this project, else its modeling kit
562
+ npx @eventmodelers/cli events --type claimed,settled # only these event types
563
+ npx @eventmodelers/cli events --slice "Add item" # a slice by id prefix or part of its title
564
+ npx @eventmodelers/cli events --tag ci --last 20 # by tag; only the last 20 that remain
565
+ npx @eventmodelers/cli events --state # what the log folds to: slices, queue, open prompts, learnings
566
+ npx @eventmodelers/cli events --json # the raw events, one per line (pipe to jq)
567
+ npx @eventmodelers/cli events --follow # keep printing as events are appended
568
+ npx @eventmodelers/cli events --kit ../other/.build-kit # another kit's log, from anywhere
569
+ npx @eventmodelers/cli events --global # the global install's log (~/.eventmodelers/kit): what runs without a project install wrote
570
+ npx @eventmodelers/cli events --global --board c2b3b7da # that log mixes every board; --board keeps one (an id prefix)
555
571
  ```
556
572
 
557
- `init --bridge --hook` persists the command to `.bridge-kit/bridge.json` — a plain, **committed** file (unlike `.eventmodelers/config.json`, which is gitignored for credentials) since the hook is project policy meant to be shared by every teammate and CI runner, not per-machine state. `bridge --hook "<command>"` overrides it for a single run without touching that file. Only one executor runs per invocation — `--local-ai`, `--hook`, and `--claude` are mutually exclusive.
558
-
559
- The hook command runs with `BRIDGE_TASK_COUNT`, `BRIDGE_SLICE_ID`/`_TITLE`/`_STATUS` (the most recent change in the batch), and `BRIDGE_BATCH_FILE` (path to the full batch as JSON) in its environment. It's invoked once per batch, not once per slice — any change that arrives while the hook is still running is left queued for the next batch rather than dropped.
560
-
561
573
  ### Claude execution & config resolution
562
574
 
563
575
  During install you can optionally point the agent at a local LLM server instead of the default
@@ -630,7 +642,7 @@ npx @eventmodelers/cli init-config --name ci-builder # name the agent this c
630
642
 
631
643
  ### Naming an agent
632
644
 
633
- Every agent identifies itself to the platform with an `agentId`, and the board's live-agent view shows that bare uuid. A **project install** mints one on first use and reuses it on every restart (`agentIds` in the project root's `.eventmodelers/config.json`). A **standalone/global run** mints a fresh one per run instead: nothing stops two ad-hoc agents running for one board, and since the heartbeat is keyed on `(token, agentId, agentType)`, a shared id would make the second agent replace the first — one agent visible however many are running, and their board writes indistinguishable.
645
+ Every agent identifies itself to the platform with an `agentId`, and the board's live-agent view shows it. An agent keeps **one id**: a **project install** mints it on first use (`agentIds` in the project root's `.eventmodelers/config.json`), a **standalone/global run** keeps one per board and repo — the directory it is started in — in `~/.eventmodelers/agent-ids.json`. It is the same agent on every restart, with its chat sessions, its star on the board and the prompts it holds. Two live processes must not share an id (the heartbeat is keyed on `(token, agentId, agentType)`, so the second would replace the first, and its start-up recovery would hand back the first one's prompts), so each run takes a small lock on its id (`.eventmodelers/locks/` in the project, `~/.eventmodelers/locks/` globally); if another live process already holds it, this run gets a fresh id and, unless you named it, an instance-marked name (`cart-service (modeling) · 3f9a`). `--id` is used as given, with no such check — for a second agent on purpose, or a supervisor that knows the id it wants. Without a name an agent shows as `<repo> (<type>)`, the repo being the `origin` remote's name or the folder.
634
646
 
635
647
  `--name` gives an agent a readable label instead of the uuid — accepted by `init`, `re-init`, and `init-config`, saved into `config.json` as `agentName`, and sent with every heartbeat from then on:
636
648
 
@@ -657,7 +669,7 @@ Pair it with `--id`: a `--standalone`/`--global` run mints a fresh id per run, s
657
669
 
658
670
  ### Backend → UI handover (`--handover`)
659
671
 
660
- 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.
672
+ 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.
661
673
 
662
674
  ```bash
663
675
  npx @eventmodelers/cli init --stack node --handover # persisted: "handover": true in .eventmodelers/config.json (also re-init)
@@ -668,18 +680,18 @@ npx @eventmodelers/cli run --handover # this run only — writes
668
680
 
669
681
  | | Handover off (default) | Handover on |
670
682
  |---|---|---|
671
- | Backend kit, STATE_VIEW / STATE_CHANGE slice | ends at `Done`, no UI contract published | publishes the UI contract, ends at `Handover` |
683
+ | Backend kit, STATE_VIEW / STATE_CHANGE slice | ends at `Done`, no UI contract published | publishes the UI contract, ends at `Waiting` |
672
684
  | Backend kit, automation / translation | `Done` | `Done` — no UI counterpart |
673
- | UI kit (`react`, `supabase-react`) | claims `Handover` only | claims `Handover` only, sets `Done` when its UI is built |
685
+ | UI kit (`react`, `supabase-react`) | claims `Waiting` only | claims `Waiting` only, sets `Done` when its UI is built |
674
686
 
675
- 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 `~/`).
687
+ 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 `~/`).
676
688
 
677
689
  **What to know before relying on it**
678
690
 
679
- - **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.
691
+ - **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.
680
692
  - **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.
681
- - **The platform must know the `Ready` / `Handover` statuses.**
682
- - A kit declares what it claims in `lib/kit.json` — `{ "pickupStatus": "Handover" }`; absent means `Planned`.
693
+ - **The platform must know the `Ready` / `Waiting` statuses.**
694
+ - A kit declares what it claims in `lib/kit.json` — `{ "pickupStatus": "Waiting" }`; absent means `Planned`.
683
695
  - `run --status-trigger <status>` overrides that pickup status for a single run (e.g. `--status-trigger Ready`); nothing is saved.
684
696
 
685
697
  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.
@@ -763,7 +775,7 @@ npx @eventmodelers/cli re-init --modeling # refresh the modeling kit
763
775
  npx @eventmodelers/cli re-init --stack supabase # override which stack to refresh from (manifest missing/stale, or switching stacks)
764
776
  ```
765
777
 
766
- `re-init` re-runs `init` against whichever stack `install-manifest.json` says was installed (no need to pass `--stack` again), but skips step 2 of `init` entirely — the root project scaffold (`package.json`, `src/`, `server.ts`, `docker-compose.yml`, etc.) and the root `CLAUDE.md` router are never touched. Use it after upgrading the CLI to pick up fixes to `ralph.js`/`ralph.sh`/skills without re-scaffolding a project you've since built on top of.
778
+ `re-init` re-runs `init` against whichever stack `install-manifest.json` says was installed (no need to pass `--stack` again), but skips step 2 of `init` entirely — the root project scaffold (`package.json`, `src/`, `server.ts`, `docker-compose.yml`, etc.) and the root `CLAUDE.md` router are never touched. Use it after upgrading the CLI to pick up fixes to `ralph.js`/skills without re-scaffolding a project you've since built on top of.
767
779
 
768
780
  You don't have to remember to: on startup every command prints a 💡 hint (on stderr) when a newer CLI is on npm, or when a kit in the current directory was installed by an older CLI than the one running — naming the exact `re-init` command to run. The npm lookup happens at most once a day (cached in `~/.eventmodelers/update-check.json`, 1.5 s timeout, silent when offline). Set `EVENTMODELERS_NO_UPDATE_CHECK=1` to turn it off; it is also off when `CI` is set.
769
781
 
package/RELEASE_NOTES.md CHANGED
@@ -1,6 +1,24 @@
1
- ## Unreleased
1
+ ## v2.0.0
2
+
3
+ ### Architecture: the kit now runs on an event log
4
+ The CLI's local state used to live in mutable files — `tasks.json` as the queue, `progress.txt` as the learnings journal, a `.claim.json` claim record, and status edited in place in `index.json`/`slice.json`. Concurrent writers, a stale board refresh or a crash could each leave those files disagreeing with the board or with each other (the failure modes are written down in `slice-state-learnings.md`). 2.0.0 replaces all of it with **event sourcing**: everything an agent does to a slice, a task, a prompt or a learning is an immutable event appended to a log, and every other file is a projection folded from that log.
5
+
6
+ - **The store.** One JSON file per event under `.build-kit/events/` (`.agent-modeling-kit/` for the modeling agent), written whole before its id is appended to `events.log`; the index line is what commits the event, so concurrent writers cannot tear the log whatever the event size. Events carry tags and `agent_id`/`organization_id`/`board_id` metadata. The store is git-ignored, and the loop's git rules now live in `.git/info/exclude` and `.git/info/attributes`, so starting an agent never dirties the tracked tree.
7
+ - **A pure core.** `fold` derives status, claim, pending outcome, attempts, blocked state, the task queue, open prompts and learnings from the event list; `decideSettle` and `decideRecovery` turn that into push/discard/release actions. Side effects sit around the fold, not inside it.
8
+ - **Slice lifecycle as events.** The loop claims a slice (`claimed`/`claim-confirmed`), settles it after the build by executing `decideSettle` (push the recorded outcome, or discard the attempt, record `attempt-failed` and put the slice back to its pickup status), and settles claims left by dead processes at the top of every iteration. Board refreshes are recorded as `board-observed`, so a stale refresh during a build can no longer revert what the agent recorded. The build agent records its outcome with `eventmodelers slice-status <status>`; the loop owns the status on the board.
9
+ - **Attempts and auto-blocks come from the log**, not from an in-memory map, so a restart no longer hands a stuck slice a fresh budget.
10
+ - **The task queue is the log.** A slice change arrives as `task-queued` (a newer change to the same slice replaces its task); the loop replays the log for the oldest task, hands it to the agent and appends `task-done`. A change landing mid-turn is just another event.
11
+ - **Prompts are journaled.** `prompt-received`/`prompt-finished` bracket every prompt the modeling agent takes; on start, what a crash left open is looked up on the platform and handed back unless it already finished. Recovery asks the platform who holds a prompt (`claimed_by`) rather than tracking process ids.
12
+ - **Learnings are events.** `eventmodelers learn` records one (topic, tags, scope; duplicates skipped) and `learn --retract` withdraws it; `learnings` lists them. `AGENTS.md` is rendered from the events and adopts lines added by hand, so regenerating never loses them. After `RALPH_CURATE_AFTER` (default 10) new learnings, a curator turn that can only merge and retract tidies the list (`learning-merged`, `learnings-curated`).
13
+ - **Look at it.** `eventmodelers events` decodes the log in the terminal — one line per event with the slice title and the writing agent; `--type`, `--slice`, `--tag`, `--last`, `--json`, `--state` (what the log folds to), `--follow`, `--kit`, `--global` and `--board` narrow or redirect it.
14
+ - **One agent id, one live process.** A standalone run keeps a stable agent id per board and repo, and takes an exclusive lock on it, so two processes never share an id, a heartbeat row or each other's prompts. A global install records nothing, so one board's learnings never surface in another's.
15
+
16
+ ### Breaking changes
17
+ - `tasks.json` is gone. Nothing reads or writes a queue file any more; the stacks' `prompt.md` files lose their queue steps and the `IDLE` reply, and `AGENT.md` is now a reference.
18
+ - `progress.txt` is gone. Learnings go through `eventmodelers learn`, a failed attempt is read from the slice's `lastAttempt`, and a block reason lives on its event.
19
+ - `run --bash` (the bash-only loop: `ralph.sh`, `lib/agent.sh`, `realtime-agent.js`) is retired. The flag is still recognised, only to say so.
20
+ - The bridge kit is retired: `init --bridge` (with `--target`/`--hook`), the `bridge` command and `uninstall --bridge-kit`. `fetch --spec-kitty` keeps working on its own.
2
21
 
3
- ### Features
4
22
  - `run --modeling`/`run --standalone` accept `--local-ai`, so the modeling agent can be driven by a local or self-hosted model instead of Claude. It was previously rejected as a build-kit-only flag, which conflated two different things: `--bash`/`--exec` select a *queue* the modeling loop has no equivalent of, while `--local-ai` selects a *model*. Both wire dialects are supported — `--local-ai` (bare) or `--local-ai ollama` for Ollama's native `POST /api/chat`, and `--local-ai vllm`/`lmstudio`/`llamacpp` for the OpenAI-compatible `POST /v1/chat/completions` that vLLM, LM Studio, llama.cpp-server, TGI and SGLang serve; anything else OpenAI-compatible works via `LOCAL_AI_URL`. The loop itself is untouched: the prompt queue, the standalone board-change lane and its damping, the idle review and the platform's full MCP tool set all behave as before, because everything Claude-specific already sat behind a single `runTurn(text)` seam. What a local model cannot bring along is the part that is not a wire format — the skills (`/place-element`, `/timeline`, `eventmodeling-*`) and the subagent fan-out are Claude Code features — so a self-directed turn does the most valuable piece of work itself, inline, and the board rules it needs come from the new runner's system prompt (`lib/modeling-local-ai.js`) instead of `.agent-modeling-kit/CLAUDE.md`. `--max-agents` is ignored in this mode. Ollama's `num_ctx` is raised to 49152 by default (the MCP tool schemas alone are ~16k tokens, well past Ollama's 4096 default, which would silently truncate the tool block), with a warning when the schemas still fill >60% of it and a turn cap of 24 tool iterations.
5
23
 
6
24
  - `run --non-interactive` never asks anything: the board and credentials that resolve from flags, `EVENTMODELERS_*` env vars and the config files are final, and an incomplete set fails the run with the reason instead of being interviewed for. A missing TTY already implied this, which covered CI and most supervisors, but not a container started with `-it` or a loop started from a terminal — stdin is a TTY nobody is watching, and the run stopped on the board confirmation or the credentials question forever. `EVENTMODELERS_TOKEN` also now answers the credentials question on its own, the way `EVENTMODELERS_BOARD_ID` always answered the board question: an env var that outranks every config file makes asking where the credentials come from moot. The README gains a container recipe, including the two things a container needs that a laptop already had — a writable HOME for the global kit and per-board credentials, and a `LOCAL_AI_URL` that isn't `localhost`.