@nebulit/embuilder 0.1.46 → 0.1.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nebulit/embuilder",
3
- "version": "0.1.46",
3
+ "version": "0.1.48",
4
4
  "description": "Event-model driven development toolkit for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,49 +1,251 @@
1
1
  ---
2
- name: skeleton-automation
3
- description: Generate automation slices from config.json
2
+ name: slice-automation
3
+ description: builds an automation slice from an event model
4
4
  ---
5
5
 
6
- # Generate Automation Slice
6
+ ### Critical understanding
7
7
 
8
- Generate automation slices (background processors with CRON scheduling) from your event model configuration.
8
+ Make sure to read the Agents.md file before building anything.
9
9
 
10
- ## Task
10
+ If the processors-Array is not empty, it´s an automation slice.
11
11
 
12
- You are tasked with generating one or more automation slices using the emmet-supabase Yeoman generator.
12
+ Important - an automation slice is "just" a state-change slice with an additional automation that triggers the command.
13
+ So also read the skill for 'state-change-slice'
13
14
 
14
- ## Steps
15
+ ## Critical Requirements
15
16
 
16
- 1. Check if a `config.json` file exists in the current directory
17
- - If not found, inform the user they need a config.json file
18
- - Exit with instructions on creating one
17
+ ### Restaurant ID Requirement
18
+ - **CRITICAL**: ALL events MUST have `restaurantId` in their metadata (camelCase)
19
+ - **NEVER** use `locationId` or `location_id` - these are outdated and forbidden
20
+ - **CRITICAL**: ALL database tables (including TODO lists) MUST have a `restaurant_id` column (snake_case)
21
+ - This ensures proper multi-tenancy and data isolation
19
22
 
20
- 2. Read the config.json to find available AUTOMATION slices:
21
- - Look for slices with `"sliceType": "AUTOMATION"`
22
- - Extract their IDs and titles
23
+ ## Overview
23
24
 
24
- 3. Show the user available automation slices and ask which ones to generate
25
- - Allow multiple selections
26
- - Or accept slice IDs from the user's original request
25
+ Automations are processes that happen in the background, based on a TODO List.
26
+ TODO Lists are always tables (read models) and each row in the table is a TODO Item.
27
27
 
28
- 4. Run the local generator with selected slices:
29
- ```bash
30
- npx yo ./.claude/skills/gen-skeleton/generators/emmet-supabase/app --action AUTOMATION --slices <slice-id-1>,<slice-id-2>
31
- ```
28
+ The automation is only responsible to:
29
+ 1. Fetch items from the TODO List (read model)
30
+ 2. Fire the command for each item
31
+ 3. Repeat on a schedule using CRON
32
32
 
33
- 5. After generation completes:
34
- - List the files that were created
35
- - Run tests for the generated slices if available
36
- - Explain the CRON configuration if applicable
37
- - Suggest next steps
33
+ ## Architecture Pattern
34
+
35
+ ```
36
+ ┌─────────────────┐
37
+ │ TODO List │ (Read Model - Inbound Dependency)
38
+ │ "items to do" │ Example: "clerks_to_invite", "items_to_fetch"
39
+ └────────┬────────┘
40
+
41
+ │ reads
42
+
43
+ ┌────────▼────────┐
44
+ │ processor.ts │ (CRON scheduled automation)
45
+ │ │ - Fetches TODO items
46
+ │ │ - Fires commands
47
+ └────────┬────────┘
48
+
49
+ │ invokes
50
+
51
+ ┌────────▼────────┐
52
+ │ CommandHandler │ (State-change logic)
53
+ │ Command.ts │ - decide() function
54
+ │ routes.ts │ - evolve() function
55
+ └─────────────────┘
56
+ ```
57
+
58
+ ## Implementation Structure
59
+
60
+ An automation slice consists of:
61
+ 1. **processor.ts** - CRON automation that fetches TODO items and fires commands
62
+ 2. **[Command]Command.ts** - Command handler with decide/evolve logic
63
+ 3. **routes.ts** - HTTP API endpoint for manual command invocation
64
+
65
+ ## Processor Configuration
66
+
67
+ Two patterns exist in the codebase:
68
+
69
+ ### Pattern 1: Using startProcessor helper (Recommended)
70
+
71
+ ```typescript
72
+ import {ProcessorConfig, ProcessorTodoItem, startProcessor} from "../../process/process";
73
+ import {handleYourCommand} from "./YourCommandCommand";
74
+
75
+ export type ItemToProcess = {
76
+ itemId: string,
77
+ // other fields from read model
78
+ }
79
+
80
+ const config: ProcessorConfig = {
81
+ schedule: "*/5 * * * * *", // Every 5 seconds (cron format)
82
+ endpoint: "your-todo-list-collection", // Read model endpoint
83
+ query: {
84
+ "status": "OPEN", // Filter criteria
85
+ "_limit": "1" // Process one at a time
86
+ }
87
+ }
88
+
89
+ const handler = async (item: ItemToProcess & ProcessorTodoItem) => {
90
+ console.log(`Processing item: ${item.itemId}`)
91
+
92
+ try {
93
+ await handleYourCommand(`aggregate-${item.itemId}`, {
94
+ type: "YourCommand",
95
+ data: {
96
+ itemId: item.itemId,
97
+ // map other fields
98
+ },
99
+ metadata: {}
100
+ })
101
+
102
+ console.log(`Successfully processed item: ${item.itemId}`)
103
+ } catch (error) {
104
+ console.error(`Error processing item ${item.itemId}:`, error)
105
+ }
106
+ }
107
+
108
+ export const processor = {
109
+ start: () => {
110
+ console.log("[YourProcessor] Starting processor...")
111
+ startProcessor<ItemToProcess>(config, handler)
112
+ }
113
+ }
114
+ ```
115
+
116
+ ### Pattern 2: Direct Supabase query (Legacy)
117
+
118
+ ```typescript
119
+ import {ProcessorConfig} from "../../process/process";
120
+ import {YourCommand, handleYourCommand} from "./YourCommandCommand";
121
+ import cron from "node-cron";
122
+ import {createServiceClient} from "../../supabase/api";
123
+
124
+ const config: ProcessorConfig = {
125
+ schedule: '*/30 * * * * *', // Every 30 seconds
126
+ endpoint: "your_todo_table", // Supabase table name
127
+ }
128
+
129
+ export const processor = {
130
+ start: () => {
131
+ cron.schedule(config.schedule, async () => {
132
+ console.log("Running process")
133
+ let client = createServiceClient()
134
+ let result = await client.from(config.endpoint).select("*")
135
+
136
+ if (result.count == 0) {
137
+ console.log(`Nothing to do for ${config.endpoint}`)
138
+ return;
139
+ }
140
+
141
+ for (const item of result.data ?? []) {
142
+ const command: YourCommand = {
143
+ type: "YourCommand",
144
+ data: {
145
+ itemId: item.itemId!
146
+ },
147
+ metadata: {}
148
+ }
149
+
150
+ const id = item.itemId
151
+ if (!id) {
152
+ throw `Cannot process Command ${command.type}. No Id available.`
153
+ }
154
+ await handleYourCommand(id, command)
155
+ }
156
+ })
157
+ }
158
+ }
159
+ ```
160
+
161
+ ## CRON Schedule Format
162
+
163
+ ```
164
+ ┌───────────── second (0-59)
165
+ │ ┌─────────── minute (0-59)
166
+ │ │ ┌───────── hour (0-23)
167
+ │ │ │ ┌─────── day of month (1-31)
168
+ │ │ │ │ ┌───── month (1-12)
169
+ │ │ │ │ │ ┌─── day of week (0-7)
170
+ │ │ │ │ │ │
171
+ * * * * * *
172
+ ```
173
+
174
+ Common schedules:
175
+ - `*/5 * * * * *` - Every 5 seconds
176
+ - `*/30 * * * * *` - Every 30 seconds
177
+ - `0 */1 * * * *` - Every minute
178
+ - `0 0 * * * *` - Every hour
179
+
180
+ ## Real Examples from Codebase
181
+
182
+ ### Example 1: ConfirmInvitation (Clerk management)
183
+
184
+ **TODO List Read Model**: `clerks_to_invite` table
185
+ **Automation**: `src/slices/ConfirmInvitation/processor.ts`
186
+ **Command**: `ConfirmInvitationCommand`
187
+ **Event Emitted**: `ClerkInvitationConfirmed`
188
+
189
+ ```typescript
190
+ // processor.ts
191
+ const config: ProcessorConfig = {
192
+ schedule: '*/30 * * * * *',
193
+ endpoint: "clerks_to_invite",
194
+ }
195
+
196
+ // Fetches clerks from TODO list and confirms their invitations
197
+ for (const clerk of result.data ?? []) {
198
+ const command: ConfirmInvitationCommand = {
199
+ type: "ConfirmInvitation",
200
+ data: {
201
+ clerkId: clerk.clerkId!
202
+ },
203
+ metadata: {}
204
+ }
205
+ await handleConfirmInvitation(clerk.clerkId, command)
206
+ }
207
+ ```
208
+
209
+ in each slice folder, generate a file .slice.json
210
+ ```
211
+ {
212
+ "id" : "<slice id>",
213
+ "slice": "<slice title>",
214
+ "context": "<contextx>",
215
+ "link": "https://miro.com/app/board/<board-id>=/?moveToWidget=<slice id>"
216
+ }
217
+ ```
218
+
219
+ ## Key Points
220
+
221
+ 1. **TODO List Naming**: Use format `<things>_to_<action>` (e.g., `clerks_to_invite`, `items_to_fetch`)
222
+ 2. **Read Model Endpoint**: TODO list is accessed via `/api/query/<name>-collection` endpoint
223
+ 3. **Processor Types**: Define TypeScript types for TODO items matching read model structure
224
+ 4. **Error Handling**: Always wrap command execution in try-catch, log errors but continue processing
225
+ 5. **Schedule Wisely**: Balance responsiveness vs system load (30 seconds is common for production)
226
+ 6. **Status Management**: TODO list typically has a `status` field ("OPEN", "PROCESSING", "DONE")
227
+ 7. **Limit Processing**: Use `_limit: "1"` to process one item at a time, preventing concurrent issues
228
+ 8. **Stream Naming**: Use aggregate-based stream names (e.g., `catalogue-${itemId}`)
229
+
230
+ ## Implementation Steps
231
+
232
+ 1. **Read the input JSON** from templates/sample-input.json
233
+ 2. **Create processor.ts** following one of the patterns above
234
+ 3. **Implement state-change slice** using the 'state-change-slice' skill
235
+ - Creates `[Command]Command.ts` with decide/evolve functions
236
+ - No `routes.ts` for Automations.
237
+ 4. **Register processor** in main application startup
238
+ 5. **Ensure TODO List exists** as a read model (separate slice handles this)
239
+
240
+ ## Sample Input Structure
241
+
242
+ Sample input: read 'templates/sample-input.json'
243
+
244
+ The input defines:
245
+ - **processors[]**: Array of automation definitions
246
+ - title: Automation name
247
+ - dependencies: Inbound (TODO list) and Outbound (Command) connections
248
+ - **commands[]**: Commands triggered by the automation
249
+ - **events[]**: Events emitted by the commands
38
250
 
39
- ## Important Notes
40
251
 
41
- - The generator is located in `.claude/skills/gen-skeleton/generators/emmet-supabase`
42
- - Multiple slices can be generated in one command (comma-separated)
43
- - Slice IDs must exactly match those in config.json
44
- - Generated files typically include:
45
- - processor.ts (background automation logic)
46
- - CRON configuration
47
- - Tests
48
- - Automation slices read from TODO lists (work queues) and fire commands on a schedule
49
- - Common use cases: auto-confirm invitations, process checkouts, send notifications