@nebulit/embuilder 0.1.47 → 0.1.49
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 +1 -1
- package/templates/backend/AGENTS.md +56 -54
- package/templates/backend/prompt.md +20 -19
- package/templates/frontend/prompt.md +11 -8
- package/templates/prompt.md +3 -2
package/package.json
CHANGED
|
@@ -104,20 +104,22 @@ import { ContextEvents } from '../../events/ContextEvents';
|
|
|
104
104
|
- Use proper `initialState` function (not empty object `{}`) in `DeciderSpecification.for()`
|
|
105
105
|
- Do NOT add explicit type arguments - let TypeScript infer (avoids TS2558)
|
|
106
106
|
- Switch statements: always use explicit `break` to prevent fallthrough bugs
|
|
107
|
+
- you must not change the signature of evolve - it has (state:State, event:Event)
|
|
108
|
+
- you must not change the signature of decide - it has (state:State, command:Command) - all data to verify the command must be passed via state.
|
|
107
109
|
|
|
108
110
|
**Structure**:
|
|
109
111
|
```typescript
|
|
110
112
|
type State = { /* track relevant state */ };
|
|
111
113
|
const initialState = (): State => ({ /* defaults */ });
|
|
112
114
|
const decide = (state: State, cmd: Command): Event[] => {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
+
if (invalidCondition) throw 'error.code';
|
|
116
|
+
return [createEvent(cmd)];
|
|
115
117
|
};
|
|
116
118
|
const evolve = (state: State, event: ContextEvents): State => {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
switch (event.type) {
|
|
120
|
+
case 'EventType': return { ...state, field: newValue };
|
|
121
|
+
default: return state;
|
|
122
|
+
}
|
|
121
123
|
};
|
|
122
124
|
```
|
|
123
125
|
|
|
@@ -133,14 +135,14 @@ const evolve = (state: State, event: ContextEvents): State => {
|
|
|
133
135
|
**Projection Structure**:
|
|
134
136
|
```typescript
|
|
135
137
|
export const ProjectionName = postgreSQLRawSQLProjection({
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
138
|
+
canHandle: ['Event1', 'Event2'],
|
|
139
|
+
evolve: (event: Event1 | Event2) => {
|
|
140
|
+
switch (event.type) {
|
|
141
|
+
case 'Event1': return sql(db(table).insert({...}).onConflict().merge());
|
|
142
|
+
case 'Event2': return sql(db(table).where({...}).delete());
|
|
143
|
+
default: return [];
|
|
144
|
+
}
|
|
142
145
|
}
|
|
143
|
-
}
|
|
144
146
|
});
|
|
145
147
|
```
|
|
146
148
|
|
|
@@ -158,22 +160,22 @@ import { createServiceClient } from '../../common/supabaseClient';
|
|
|
158
160
|
const config = { schedule: '*/30 * * * * *', endpoint: "work_queue_table" };
|
|
159
161
|
|
|
160
162
|
export const processor = {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
163
|
+
start: () => {
|
|
164
|
+
cron.schedule(config.schedule, async () => {
|
|
165
|
+
const client = createServiceClient();
|
|
166
|
+
const result = await client.from(config.endpoint)
|
|
167
|
+
.select("*").eq('must_process', true).limit(1);
|
|
168
|
+
|
|
169
|
+
if (result.error) return;
|
|
170
|
+
for (const item of result.data ?? []) {
|
|
171
|
+
try {
|
|
172
|
+
await handleCommand(streamId, command, { userId: 'system', ...metadata });
|
|
173
|
+
} catch (error) {
|
|
174
|
+
console.error('Processing error:', error);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
177
179
|
};
|
|
178
180
|
```
|
|
179
181
|
|
|
@@ -189,9 +191,9 @@ export const processor = {
|
|
|
189
191
|
```typescript
|
|
190
192
|
type State = { isActive: boolean };
|
|
191
193
|
const decide = (state: State, cmd: Command): Event[] => {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
194
|
+
if (state.isActive) throw "already.active"; // Activate guard
|
|
195
|
+
if (!state.isActive) throw "not.active"; // Deactivate guard
|
|
196
|
+
return [event];
|
|
195
197
|
};
|
|
196
198
|
```
|
|
197
199
|
|
|
@@ -201,13 +203,13 @@ const decide = (state: State, cmd: Command): Event[] => {
|
|
|
201
203
|
```typescript
|
|
202
204
|
type State = { trackedIds: Set<string> };
|
|
203
205
|
const evolve = (state: State, event: ContextEvents): State => {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
206
|
+
switch (event.type) {
|
|
207
|
+
case 'ItemAdded': return { trackedIds: new Set([...state.trackedIds, event.data.id]) };
|
|
208
|
+
case 'ItemRemoved':
|
|
209
|
+
const newSet = new Set(state.trackedIds);
|
|
210
|
+
newSet.delete(event.data.id);
|
|
211
|
+
return { trackedIds: newSet };
|
|
212
|
+
}
|
|
211
213
|
};
|
|
212
214
|
```
|
|
213
215
|
|
|
@@ -217,10 +219,10 @@ const evolve = (state: State, event: ContextEvents): State => {
|
|
|
217
219
|
```typescript
|
|
218
220
|
type State = { submitted: boolean; reverted: boolean; approved: boolean; declined: boolean };
|
|
219
221
|
const decide = (state: State, cmd: Command): Event[] => {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
222
|
+
if (state.submitted) throw 'cannot submit twice';
|
|
223
|
+
if (!state.submitted) throw 'not_submitted';
|
|
224
|
+
if (state.reverted) throw 'already_reverted';
|
|
225
|
+
return [event];
|
|
224
226
|
};
|
|
225
227
|
```
|
|
226
228
|
|
|
@@ -275,27 +277,27 @@ All endpoints must:
|
|
|
275
277
|
### STATE_CHANGE Tests
|
|
276
278
|
```typescript
|
|
277
279
|
DeciderSpecification.for(decide, evolve, initialState)
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
280
|
+
.given([PrerequisiteEvent1, PrerequisiteEvent2])
|
|
281
|
+
.when(Command({ field: 'value' }))
|
|
282
|
+
.then([ExpectedEvent({ field: 'value' })]);
|
|
281
283
|
```
|
|
282
284
|
|
|
283
285
|
### STATE_VIEW Tests
|
|
284
286
|
```typescript
|
|
285
287
|
PostgreSQLProjectionSpec.for(ProjectionName)
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
288
|
+
.given([Event1, Event2])
|
|
289
|
+
.when([]) // Always empty
|
|
290
|
+
.then(async (state) => {
|
|
291
|
+
const result = await state.query();
|
|
292
|
+
assert.equal(result[0].field, expectedValue);
|
|
293
|
+
});
|
|
292
294
|
```
|
|
293
295
|
|
|
294
296
|
### Error Case Tests
|
|
295
297
|
```typescript
|
|
296
298
|
.given([EventCreated])
|
|
297
|
-
.when(Command)
|
|
298
|
-
.shouldFail(); // Expects error
|
|
299
|
+
.when(Command)
|
|
300
|
+
.shouldFail(); // Expects error
|
|
299
301
|
```
|
|
300
302
|
|
|
301
303
|
### Test Coverage
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
You are an autonomous coding agent working on the backend of a software project. You apply your skills to build software slices. You only work on one slice at a time.
|
|
4
4
|
|
|
5
5
|
The structure defined in the Project-Skills is relevant.
|
|
6
|
+
If a slice is in status 'planned', Even if a slice seems to have been implemented, make sure to verify the implementation, check for new fields, new specifications, additional custom prompts in the slice json. A "planned" slice can also be an update of an existing slice. If that is the case, match the implemenetation to the updated slice definition.
|
|
7
|
+
|
|
6
8
|
|
|
7
9
|
## Your Task
|
|
8
10
|
|
|
@@ -10,27 +12,26 @@ The structure defined in the Project-Skills is relevant.
|
|
|
10
12
|
1. Read the description at `.slices/index.json` (in the same directory as this file). Every item in status "planned" and assigned to "backend_worker" is a task.
|
|
11
13
|
2. Read the progress log at `progress.txt` (check Codebase Patterns section first)
|
|
12
14
|
3. Make sure you are on the right branch "feature/<slicename>", if unsure, start from main.
|
|
13
|
-
5. Pick the **highest priority** task assigned to backend_worker. This becomes your
|
|
15
|
+
5. Pick the **highest priority** task assigned to backend_worker. This becomes your only task to work on. Set the status "InProgress", add a started_date ( including date and time ) in the index.json. If no slice has status planned, reply with:
|
|
14
16
|
<promise>NO_TASKS</promise> and stop. Do not work on other slices.
|
|
15
|
-
6. Pick the slice definition from the project root /.slices in <folder> defined in the prd.
|
|
16
|
-
7. A slice can define additional prompts as codegen/backendPrompt. any additional prompts defined in
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
6. Pick the slice definition from the project root /.slices in <folder> defined in the prd. Only work on this one assigned task, never other tasks.
|
|
18
|
+
7. A slice can define additional prompts as codegen/backendPrompt in the slice.json. any additional prompts defined in json are hints for the implementation of the slice and have to be taken into account. If you use the additional prompt, add a line in progress.txt
|
|
19
|
+
8. Define the slice type. If the processors-array is not empty, it´s an automation slice. if there is a readmodel, it´s a state view. If there is a command and no processor, it´s a state change. Load the matching skill (slice-automation, slice-state-change, slice-state-view)
|
|
20
|
+
9. Write a short progress one liner after each step to progress.txt
|
|
21
|
+
10. Analyze and Implement that single slice, make use of the skills in the skills directory, but also your previsously collected
|
|
22
|
+
knowledge. Make a list TODO list for what needs to be done. Also make sure to adjust the implementation according to the json definition, whenever you work on a task. The definition can change for existing slices, which means the implementation has to adapt. Carefully inspect events, fields and compare against the implemented slice. JSON is the desired state. ATTENTION: A "planned" task can also be just added specifications. So always look at the slice itself, but also the specifications. If specifications were added in json, which are not on code, you need to add them in code.
|
|
23
|
+
11. The slice in the json is always true, the code follows what is defined in the json
|
|
24
|
+
12. the backend of a slice is only 'Done' if business logic is implemented as defined in the JSON, APIs are implemented, all scenarios in JSON are implemented in code and it
|
|
23
25
|
fulfills the slice.json. There must be no specification in json, that has no equivalent in code.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
20. Finish the iteration.
|
|
26
|
+
13. make sure to write the ui-prompt.md for the ui_worker as defined if defined in the skill
|
|
27
|
+
14. Run quality checks ( npm run build, npm run test ) - Attention - it´s enough to run the tests for the slice. Do not run all tests.
|
|
28
|
+
15. even if the slice is fully implemented, run your test-analyzer skill and provide the code-slice.json file as defined in the skill
|
|
29
|
+
16. if the slice is an automation slice, set status to 'Done'. Otherwise - Update the Slice in the index.json back to status 'Planned' and assign the 'ui-worker'
|
|
30
|
+
17. If checks pass, commit ALL changes with message: `feat: [Slice Name]`.
|
|
31
|
+
18. Append your progress to `progress.txt` after each step in the iteration.
|
|
32
|
+
19. append your new learnings to AGENTS.md in a compressed form, reusable for future iterations. Only add learnings if they are not already there.
|
|
33
|
+
20. if the slice status is 'Done', merge back to main.
|
|
34
|
+
21. Finish the iteration.
|
|
34
35
|
|
|
35
36
|
## Progress Report Format
|
|
36
37
|
|
|
@@ -4,11 +4,13 @@ Build React + TypeScript UI components from slice JSON definitions using establi
|
|
|
4
4
|
|
|
5
5
|
## Your Task
|
|
6
6
|
|
|
7
|
+
If a slice is in status 'planned', Even if a slice seems to have been implemented, make sure to verify the implementation, check for new fields, additional custom prompts in the slice json. A "planned" slice can also be an update of an existing slice. If that is the case, match the implemenetation to the updated slice definition.
|
|
8
|
+
|
|
7
9
|
0. Do not read the entire code base. read /frontend/AGENTS.md. Focus on the tasks in this description.
|
|
8
10
|
1. Read the description at `.slices/index.json` (in the same directory as this file). Every item in status "planned" and assigned to "ui_worker" is a task.
|
|
9
11
|
2. Read the progress log at `progress.txt` (check Codebase Patterns section first)
|
|
10
12
|
3. Make sure you are on the right branch "feature/<slicename>", the branch should exist.
|
|
11
|
-
5. Pick the **highest priority** task assigned to ui_worker. This becomes your
|
|
13
|
+
5. Pick the **highest priority** task assigned to ui_worker. This becomes your task. Set the status "InProgress", add a started_date ( including date and time ) in the index.json. If no slice has status planned, reply with:
|
|
12
14
|
<promise>NO_TASKS</promise> and stop. Do not work on other slices.
|
|
13
15
|
6. Pick the slice definition from the project root /.slices in <folder> defined in the prd. Never work on more than one slice per iteration.
|
|
14
16
|
7. A slice can define additional prompts as codegen/uiPrompt. any additional prompts defined in backend are hints for the implementation of the slice and have to be taken into account. If you use the additional prompt, add a line in progress.txt
|
|
@@ -16,15 +18,15 @@ Build React + TypeScript UI components from slice JSON definitions using establi
|
|
|
16
18
|
9. Analyze and Implement according to the Rest of the instructions in this file, make use of the skills in the skills directory, but also your previsously collected
|
|
17
19
|
knowledge. Make a list TODO list for what needs to be done. Also make sure to adjust the implementation according to the json definition.
|
|
18
20
|
10. The slice in the json is always true, the code follows what is defined in the json
|
|
19
|
-
11.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
11. make sure to read the ui-prompt.md in /backend/src/slices/<slice>/
|
|
22
|
+
13. carefully read custom uiPrompt in the slice.json and follow the instructions. If no mount point is defined in uiPrompt, verify if there is natural place where to mount the component. If there is no natural place, create a new page, name it after the Component and define a Route in App.tsx
|
|
23
|
+
14. Run quality checks ( npm run build, tsc ) - Attention - the slice is only done if the component is mounted and testable.
|
|
24
|
+
15. Make sure to style components according to the style guides.
|
|
23
25
|
16. Update the Slice in the index.json to status 'Done' and remove assignment
|
|
24
|
-
17.
|
|
26
|
+
17. Append your progress to `progress.txt` after each step in the iteration.
|
|
27
|
+
18. append your new learnings to frontend/AGENTS.md in a compressed form, reusable for future iterations. Only add learnings if they are not already there.
|
|
28
|
+
19. If checks pass, commit ALL changes with message: `feat: [Slice Name]` and merge back to main as FF merge ( update
|
|
25
29
|
first )
|
|
26
|
-
18. Append your progress to `progress.txt` after each step in the iteration.
|
|
27
|
-
19. append your new learnings to frontend/AGENTS.md in a compressed form, reusable for future iterations. Only add learnings if they are not already there.
|
|
28
30
|
20. Finish the iteration.
|
|
29
31
|
|
|
30
32
|
---
|
|
@@ -42,6 +44,7 @@ This project has custom skills available in `.claude/skills/` that automate comm
|
|
|
42
44
|
- **`/ui-scaffold-component`**: Scaffold React components (List, Dialog, Page) using Bulma CSS
|
|
43
45
|
|
|
44
46
|
**IMPORTANT**: When asked to build UI from slices, USE the `/ui-build-slice-ui` skill first! It will orchestrate all other skills in the correct order. Only use individual skills when you need to regenerate or modify a specific part.
|
|
47
|
+
**IMPORTANT**: Unless stated in a custom prompt, components read directly from the tables provided, not from api-endpoints via HTTP
|
|
45
48
|
|
|
46
49
|
---
|
|
47
50
|
|
package/templates/prompt.md
CHANGED
|
@@ -5,8 +5,9 @@ You assign tasks to workers. Only one task can be assigned and in status planned
|
|
|
5
5
|
|
|
6
6
|
## Your Task
|
|
7
7
|
|
|
8
|
-
1. find the most important next slice by
|
|
8
|
+
1. find the most important next slice by reading .slices/index.json
|
|
9
9
|
2. if the slice is in status planned and not assigned, assign it to backend_worker (property "assigned" ) and continue with backend/prompt.md. Ignore the rest of this file.
|
|
10
10
|
3. if the status is in status "InProgress" and assigned to backend_worker, updated started_time and continue with backend/prompt.md. Ignore the rest of this file.
|
|
11
11
|
4. if the status is "done" and assigned to "backend_worker", assign the task to "ui_worker" and move it back to status "planned". continue with frontend/prompt.md. Ignore the rest of this file.
|
|
12
|
-
|
|
12
|
+
5. if there is no task in status planned, return <promise>NO_TASKS</promise>
|
|
13
|
+
6. If one task is done. Finish your work, do not continue with the next slice.
|