@eventmodelers/node-kit 0.0.11 → 0.0.12
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/.claude/skills/build-automation/SKILL.md +260 -0
- package/templates/.claude/skills/build-state-change/SKILL.md +329 -0
- package/templates/.claude/skills/build-state-view/SKILL.md +384 -0
- package/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +609 -0
- package/templates/.claude/skills/load-slice/SKILL.md +69 -14
- package/templates/realtime-agent/src/index.js +11 -1
- package/templates/root/.env.example +22 -0
- package/templates/root/Claude.md +58 -0
- package/templates/root/agent.sh +15 -0
- package/templates/root/backend-prompt.md +139 -0
- package/templates/root/flyway.conf +17 -0
- package/templates/root/package.json +52 -0
- package/templates/root/ralph.sh +47 -26
- package/templates/root/server.ts +213 -0
- package/templates/root/setup-env.sh +55 -0
- package/templates/root/src/common/assertions.ts +6 -0
- package/templates/root/src/common/db.ts +32 -0
- package/templates/root/src/common/loadPostgresEventstore.ts +39 -0
- package/templates/root/src/common/parseEndpoint.ts +51 -0
- package/templates/root/src/common/processorDlq.ts +28 -0
- package/templates/root/src/common/realtimeBroadcast.ts +19 -0
- package/templates/root/src/common/replay.ts +16 -0
- package/templates/root/src/common/routes.ts +19 -0
- package/templates/root/src/common/testHelpers.ts +54 -0
- package/templates/root/src/slices/example/routes.ts +134 -0
- package/templates/root/src/supabase/LoginHandler.ts +36 -0
- package/templates/root/src/supabase/ProtectedPageProps.ts +21 -0
- package/templates/root/src/supabase/README.md +171 -0
- package/templates/root/src/supabase/api.ts +56 -0
- package/templates/root/src/supabase/component.ts +12 -0
- package/templates/root/src/supabase/requireOrgaAdmin.ts +32 -0
- package/templates/root/src/supabase/requireUser.ts +72 -0
- package/templates/root/src/supabase/serverProps.ts +25 -0
- package/templates/root/src/supabase/staticProps.ts +10 -0
- package/templates/root/src/swagger.ts +34 -0
- package/templates/root/src/util/assertions.ts +6 -0
- package/templates/root/src/util/hash.ts +9 -0
- package/templates/root/src/util/sanitize.ts +23 -0
- package/templates/root/supabase/config.toml +295 -0
- package/templates/root/supabase/migrations/V1__schema.sql.example +12 -0
- package/templates/root/supabase/seed.sql +1 -0
- package/templates/root/tsconfig.json +32 -0
- package/templates/root/vercel.json +8 -0
- package/templates/root/model.md +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: load-slice
|
|
3
|
-
description: Load all slices from the board via the slicedata API and
|
|
3
|
+
description: Load all slices from the board via the slicedata API and persist them to the .slices/ directory hierarchy (index.json with full definitions, per-slice folders). Returns data for a specific slice by ID or title.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Load Slice
|
|
@@ -18,7 +18,7 @@ From `$ARGUMENTS`, extract:
|
|
|
18
18
|
| `sliceId` | UUID of the slice (SLICE_BORDER node ID) | optional — prefer over title |
|
|
19
19
|
| `sliceTitle` | slice title (case-insensitive match) | optional — used if sliceId missing |
|
|
20
20
|
|
|
21
|
-
If neither is provided,
|
|
21
|
+
If neither is provided, load and persist all slices without filtering.
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
@@ -32,26 +32,78 @@ curl -s \
|
|
|
32
32
|
"<BASE_URL>/api/org/<ORG_ID>/boards/<BOARD_ID>/slicedata/slices"
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
Response: `{ "slices": [{ "id": "
|
|
35
|
+
Response shape: `{ "slices": [ { "id": "...", "title": "...", "status": "...", "context": "...", ... } ] }`
|
|
36
36
|
|
|
37
37
|
Save the full array as `ALL_SLICES`.
|
|
38
38
|
|
|
39
39
|
---
|
|
40
40
|
|
|
41
|
-
## Step 3 — Persist
|
|
41
|
+
## Step 3 — Persist slices to .slices/ directory
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
Apply the following logic for every slice in `ALL_SLICES`.
|
|
44
|
+
|
|
45
|
+
### Derive paths
|
|
46
|
+
|
|
47
|
+
- `contextName` = `slice.context` if present, otherwise `"default"` — **preserve original casing** (e.g. `"Beta"`, not `"beta"`)
|
|
48
|
+
- `sliceFolder` = `slice.title` lowercased, with all spaces removed and the prefix `"slice:"` stripped
|
|
49
|
+
e.g. `"Beta Enable User for Beta Test"` → `"betaenableuserforbetatest"`
|
|
50
|
+
- `baseFolder` = `.slices/<contextName>/`
|
|
51
|
+
- `sliceDir` = `.slices/<contextName>/<sliceFolder>/`
|
|
52
|
+
|
|
53
|
+
### Write files
|
|
44
54
|
|
|
45
55
|
```bash
|
|
46
|
-
mkdir -p slices
|
|
56
|
+
mkdir -p ".slices/<contextName>/<sliceFolder>"
|
|
47
57
|
```
|
|
48
58
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
59
|
+
**`.slices/current_context.json`** — always overwrite:
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{ "name": "Beta" }
|
|
52
63
|
```
|
|
53
64
|
|
|
54
|
-
|
|
65
|
+
**`.slices/<contextName>/context.json`** — write once per context:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{ "name": "Beta" }
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**`.slices/<contextName>/<sliceFolder>/slice.json`** — the full slice object with the `index` field removed.
|
|
72
|
+
|
|
73
|
+
### Maintain `.slices/<contextName>/index.json`
|
|
74
|
+
|
|
75
|
+
Read the file if it exists, otherwise start with `{ "slices": [] }`.
|
|
76
|
+
|
|
77
|
+
Each entry in `index.json` contains the index metadata **plus** the complete slice definition fetched from the API:
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"slices": [
|
|
82
|
+
{
|
|
83
|
+
"id": "d0dbc70c-f244-4048-886b-1d11e461f466",
|
|
84
|
+
"slice": "Beta Enable User for Beta Test",
|
|
85
|
+
"index": 0,
|
|
86
|
+
"context": "Beta",
|
|
87
|
+
"folder": "betaenableuserforbetatest",
|
|
88
|
+
"status": "Created",
|
|
89
|
+
"definition": {
|
|
90
|
+
"id": "d0dbc70c-f244-4048-886b-1d11e461f466",
|
|
91
|
+
"title": "Beta Enable User for Beta Test",
|
|
92
|
+
"status": "Created",
|
|
93
|
+
"context": "Beta"
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The `definition` field holds the full object returned by the API for that slice (all fields as-is).
|
|
101
|
+
|
|
102
|
+
**Merge rules:**
|
|
103
|
+
- If an entry with the same `id` already exists: update all fields and refresh `definition`; preserve any existing `assigned` field.
|
|
104
|
+
- If not found: append the new entry.
|
|
105
|
+
|
|
106
|
+
Write the updated object back to `.slices/<contextName>/index.json`.
|
|
55
107
|
|
|
56
108
|
---
|
|
57
109
|
|
|
@@ -68,19 +120,22 @@ If a specific slice was requested but not found, stop and list the available tit
|
|
|
68
120
|
## Step 5 — Output
|
|
69
121
|
|
|
70
122
|
```
|
|
71
|
-
Slices loaded: <count> total
|
|
123
|
+
Slices loaded: <count> total
|
|
124
|
+
Persisted to: .slices/<contextName>/
|
|
72
125
|
|
|
73
126
|
Requested slice:
|
|
74
127
|
Title: <title>
|
|
75
128
|
ID: <id>
|
|
76
129
|
Status: <status>
|
|
130
|
+
Folder: .slices/<contextName>/<sliceFolder>/slice.json
|
|
77
131
|
```
|
|
78
132
|
|
|
79
133
|
Or if no filter was given:
|
|
134
|
+
|
|
80
135
|
```
|
|
81
|
-
All slices (<count>):
|
|
82
|
-
- <title> [<status>]
|
|
136
|
+
All slices (<count>) — context: <contextName>:
|
|
137
|
+
- <title> [<status>] → .slices/<contextName>/<sliceFolder>/
|
|
83
138
|
- ...
|
|
84
139
|
```
|
|
85
140
|
|
|
86
|
-
Make the matched slice's `id`, `title`,
|
|
141
|
+
Make the matched slice's `id`, `title`, `status`, and local folder path available to subsequent steps in the same session.
|
|
@@ -6,6 +6,16 @@ import { randomUUID } from 'crypto';
|
|
|
6
6
|
|
|
7
7
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
8
|
|
|
9
|
+
function findRalphShDir(startDir) {
|
|
10
|
+
let dir = startDir;
|
|
11
|
+
while (true) {
|
|
12
|
+
if (existsSync(join(dir, 'ralph.sh'))) return dir;
|
|
13
|
+
const parent = dirname(dir);
|
|
14
|
+
if (parent === dir) return null;
|
|
15
|
+
dir = parent;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
9
19
|
function findConfigPath(startDir) {
|
|
10
20
|
let dir = startDir;
|
|
11
21
|
while (true) {
|
|
@@ -91,7 +101,7 @@ async function writeTask(payload, cwd) {
|
|
|
91
101
|
}
|
|
92
102
|
|
|
93
103
|
async function start() {
|
|
94
|
-
const claudeCwd = process.argv[2] ?? resolve(process.cwd(), '
|
|
104
|
+
const claudeCwd = process.argv[2] ?? findRalphShDir(process.cwd()) ?? resolve(process.cwd(), '.');
|
|
95
105
|
|
|
96
106
|
const local = loadLocalConfig();
|
|
97
107
|
const cfg = await fetchPlatformConfig(local);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Supabase
|
|
2
|
+
SUPABASE_URL=https://<project-id>.supabase.co
|
|
3
|
+
SUPABASE_PUBLISHABLE_KEY=<supabase-anon-key>
|
|
4
|
+
SUPABASE_SECRET_KEY=<supabase-service-role-key>
|
|
5
|
+
SUPABASE_DB_URL=postgresql://postgres.<project-id>:<db-password>@aws-1-eu-central-1.pooler.supabase.com:5432/postgres?prepareThreshold=0
|
|
6
|
+
|
|
7
|
+
# Frontend (Next.js public vars)
|
|
8
|
+
NEXT_PUBLIC_SUPABASE_URL=https://<project-id>.supabase.co
|
|
9
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY=<supabase-anon-key>
|
|
10
|
+
|
|
11
|
+
# Server
|
|
12
|
+
PORT=3000
|
|
13
|
+
API_URL=http://localhost:3000
|
|
14
|
+
BACKEND_URL=http://localhost:3000
|
|
15
|
+
|
|
16
|
+
# Flyway (database migrations)
|
|
17
|
+
FLYWAY_URL=jdbc:postgresql://aws-1-eu-west-1.pooler.supabase.com:6543/postgres?user=postgres.<project-id>&password=<db-password>
|
|
18
|
+
FLYWAY_USER=postgres.<project-id>
|
|
19
|
+
FLYWAY_PASSWORD=<db-password>
|
|
20
|
+
|
|
21
|
+
# Optional
|
|
22
|
+
TESTING=false
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Project Configuration
|
|
2
|
+
|
|
3
|
+
Read Events in src/events to understand the global structure.
|
|
4
|
+
|
|
5
|
+
## Framework & Styling
|
|
6
|
+
|
|
7
|
+
- **CSS Framework**: Use Bulma CSS exclusively for all styling
|
|
8
|
+
- **Assumption**: Bulma CSS is already available and imported in the project
|
|
9
|
+
- **Styling Guidelines**:
|
|
10
|
+
- Use Bulma's utility classes and components
|
|
11
|
+
- Follow Bulma's naming conventions and class structure
|
|
12
|
+
- Leverage Bulma's responsive design features
|
|
13
|
+
- Prefer Bulma components over custom CSS
|
|
14
|
+
|
|
15
|
+
## File Structure Constraints
|
|
16
|
+
|
|
17
|
+
- **Strict Path Limitation**: if not instructed otherwise, only check `src/slices/{slicename}/*.ts`
|
|
18
|
+
- **Slice Organization**: Each feature/domain should be organized as a separate slice
|
|
19
|
+
|
|
20
|
+
## Code Standards
|
|
21
|
+
|
|
22
|
+
- **Language**: TypeScript only
|
|
23
|
+
- **Module System**: Use ES modules (import/export)
|
|
24
|
+
- **Type Safety**: Ensure all code is properly typed
|
|
25
|
+
|
|
26
|
+
## Development Guidelines
|
|
27
|
+
|
|
28
|
+
1. Each slice should be self-contained and focused on a specific domain
|
|
29
|
+
2. Use Bulma's grid system, components, and utilities for all UI-related code
|
|
30
|
+
3. Maintain clear separation of concerns within each slice
|
|
31
|
+
4. Follow TypeScript best practices for type definitions and interfaces
|
|
32
|
+
|
|
33
|
+
Only check src/slices/{slice}/*.ts, do not check subfolders, if not explicitely tasked to build the UI.
|
|
34
|
+
If not tasked explicitely to change routes, ignore routes*.ts
|
|
35
|
+
|
|
36
|
+
Ignore case for files and slices in prompts. "CartItems" slice is the same as "cartitemsrun t"
|
|
37
|
+
|
|
38
|
+
Do not change files with tests unless explicitely instructed: *.test.ts
|
|
39
|
+
|
|
40
|
+
After you are done, automatically run the tests for the slice that was edited.
|
|
41
|
+
|
|
42
|
+
## Example Slice Structure
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
src/slices/
|
|
46
|
+
├── {slice-name}/
|
|
47
|
+
│ ├── CommandHandler.ts
|
|
48
|
+
│ ├── ui/
|
|
49
|
+
│ └── routes.ts
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Bulma Integration Notes
|
|
53
|
+
|
|
54
|
+
- Utilize Bulma's component library: navbar, cards, buttons, forms, modals, etc.
|
|
55
|
+
- Apply Bulma's spacing utilities: `m-*`, `p-*`, `has-text-*`, `has-background-*`
|
|
56
|
+
- Use Bulma's flexbox utilities for layouts
|
|
57
|
+
- Implement responsive design with Bulma's breakpoint classes
|
|
58
|
+
- Leverage Bulma's color palette and typography classes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Runs the AI agent with the given prompt.
|
|
3
|
+
# Usage: ./agent.sh "<prompt>"
|
|
4
|
+
# Override by replacing this script with your own implementation.
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
PROMPT="${1:-}"
|
|
9
|
+
|
|
10
|
+
if [[ -z "$PROMPT" ]]; then
|
|
11
|
+
echo "ERROR: No prompt provided"
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
claude "$PROMPT"
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Ralph Agent Instructions
|
|
2
|
+
|
|
3
|
+
You are an autonomous coding agent working on a software project. You apply your skills to build software slices. You only work on one slice at a time.
|
|
4
|
+
|
|
5
|
+
The structure defined in the Project-Skills is relevant.
|
|
6
|
+
|
|
7
|
+
## Your Task
|
|
8
|
+
|
|
9
|
+
0. Do not read the entire code base. Focus on the tasks in this description.
|
|
10
|
+
1. Read the description at `.slices/index.json` (in the same directory as this file). Every item in status "planned" is a task.
|
|
11
|
+
2. Read the progress log at `progress.txt` (check Codebase Patterns section first)
|
|
12
|
+
3. Make sure you are on the right branch "feature/<slicename>", if unsure, start from main.
|
|
13
|
+
5. Pick the **highest priority** slice where status is "planned" ( case insensitive ). This becomes your PRD. Set the status "InProgress" in the index.json. If no slice has status planned, reply with:
|
|
14
|
+
<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. Never work on more than one slice per iteration.
|
|
16
|
+
7. A slice can define additional prompts as codegen/backendPrompt. 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
|
|
17
|
+
7. Define the slice type and load the matching skill. If the processors-array is not empty, it´s an automation slice.
|
|
18
|
+
8. Write a short progress one liner after each step to progress.txt
|
|
19
|
+
9. Analyze and Implement that single slice, make use of the skills in the skills directory, but also your previsously collected
|
|
20
|
+
knowledge. Make a list TODO list for what needs to be done. Also make sure to adjust the implementation according to the json definition. 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.
|
|
21
|
+
10. The slice in the json is always true, the code follows what is defined in the json
|
|
22
|
+
11. 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
|
+
fulfills the slice.json. There must be no specification in json, that has no equivalent in code.
|
|
24
|
+
12. make sure to write the ui-prompt.md as defined if defined in the skill
|
|
25
|
+
13. 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.
|
|
26
|
+
14. even if the slice is fully implemented, run your test-analyzer skill and provide the code-slice.json file as defined in the skill
|
|
27
|
+
15. If checks pass, commit ALL changes with message: `feat: [Slice Name]` and merge back to main as FF merge ( update
|
|
28
|
+
first )
|
|
29
|
+
16. Update the PRD to set `status: Done` for the completed story.
|
|
30
|
+
17. Append your progress to `progress.txt` after each step in the iteration.
|
|
31
|
+
18. append your new learnings to AGENTS.md in a compressed form, reusable for future iterations. Only add learnings if they are not already there.
|
|
32
|
+
19. Finish the iteration.
|
|
33
|
+
|
|
34
|
+
## Progress Report Format
|
|
35
|
+
|
|
36
|
+
APPEND to progress.txt (never replace, always append):
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
## [Date/Time] - [Slice]
|
|
40
|
+
|
|
41
|
+
- What was implemented
|
|
42
|
+
- Files changed
|
|
43
|
+
- **Learnings for future iterations:**
|
|
44
|
+
- Patterns discovered (e.g., "this codebase uses X for Y")
|
|
45
|
+
- Gotchas encountered (e.g., "don't forget to update Z when changing W")
|
|
46
|
+
- Useful context (e.g., "the evaluation panel is in component X")
|
|
47
|
+
---
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The learnings section is critical - it helps future iterations avoid repeating mistakes and understand the codebase
|
|
51
|
+
better.
|
|
52
|
+
|
|
53
|
+
## Consolidate Patterns
|
|
54
|
+
|
|
55
|
+
If you discover a **reusable pattern** that future iterations should know, add it to the `## Codebase Patterns` section
|
|
56
|
+
at the TOP of progress.txt (create it if it doesn't exist). This section should consolidate the most important
|
|
57
|
+
learnings:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
## Codebase Patterns
|
|
61
|
+
- Example: Use `sql<number>` template for aggregations
|
|
62
|
+
- Example: Always use `IF NOT EXISTS` for migrations
|
|
63
|
+
- Example: Export types from actions.ts for UI components
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Only add patterns that are **general and reusable**, not story-specific details.
|
|
67
|
+
|
|
68
|
+
## Update AGENTS.md Files
|
|
69
|
+
|
|
70
|
+
Before committing, check if any edited files have learnings worth preserving in nearby AGENTS.md files:
|
|
71
|
+
|
|
72
|
+
1. **Identify directories with edited files** - Look at which directories you modified
|
|
73
|
+
3. **Add valuable learnings that apply to all tasks** to the Agents.md - If you discovered something future developers/agents should know:
|
|
74
|
+
- API patterns or conventions specific to that module
|
|
75
|
+
- Gotchas or non-obvious requirements
|
|
76
|
+
- Dependencies between files
|
|
77
|
+
- Testing approaches for that area
|
|
78
|
+
- Configuration or environment requirements
|
|
79
|
+
|
|
80
|
+
**Examples of good AGENTS.md additions:**
|
|
81
|
+
|
|
82
|
+
- "When modifying X, also update Y to keep them in sync"
|
|
83
|
+
- "This module uses pattern Z for all API calls"
|
|
84
|
+
- "Tests require the dev server running on PORT 3000"
|
|
85
|
+
- "Field names must match the template exactly"
|
|
86
|
+
|
|
87
|
+
**Do NOT add:**
|
|
88
|
+
|
|
89
|
+
- Slice specific implementation details
|
|
90
|
+
- Story-specific implementation details
|
|
91
|
+
- Temporary debugging notes
|
|
92
|
+
- Information already in progress.txt
|
|
93
|
+
- Task speecific learnings like "- Timesheet approval requires: submitted=true, reverted=false, approved=false, declined=false"
|
|
94
|
+
|
|
95
|
+
Only update AGENTS.md if you have **genuinely reusable knowledge** that would help future work
|
|
96
|
+
|
|
97
|
+
## Quality Requirements
|
|
98
|
+
|
|
99
|
+
- ALL commits must pass your project's quality checks (typecheck, lint, test)
|
|
100
|
+
- run 'npm run build'
|
|
101
|
+
- run 'npm run test'
|
|
102
|
+
- Do NOT commit broken code
|
|
103
|
+
- Keep changes focused and minimal
|
|
104
|
+
- Follow existing code patterns
|
|
105
|
+
|
|
106
|
+
## Skills
|
|
107
|
+
|
|
108
|
+
Use the provided skills in the skills folder as guidance.
|
|
109
|
+
Update skill definitions if you find an improvement you can make.
|
|
110
|
+
|
|
111
|
+
## Specifications
|
|
112
|
+
|
|
113
|
+
For every specification added to the Slice, you need to implement one use executable Specification in Code.
|
|
114
|
+
|
|
115
|
+
A Slice is not complete if specifications are missing or can´t be executed.
|
|
116
|
+
|
|
117
|
+
## Stop Condition
|
|
118
|
+
|
|
119
|
+
After completing a user story, check if ALL slices have `status: Done`.
|
|
120
|
+
|
|
121
|
+
If ALL stories are complete and passing, reply with:
|
|
122
|
+
<promise>COMPLETE</promise>
|
|
123
|
+
|
|
124
|
+
If there are no stories with `status: Planned` (but not all are Done), reply with:
|
|
125
|
+
<promise>NO_TASKS</promise>
|
|
126
|
+
|
|
127
|
+
If there are still stories with `status: Planned`, end your response normally (another iteration will pick up the next
|
|
128
|
+
story).
|
|
129
|
+
|
|
130
|
+
## Important
|
|
131
|
+
|
|
132
|
+
- Work on ONE slice per iteration
|
|
133
|
+
- Commit frequently
|
|
134
|
+
- update progress.txt frequently
|
|
135
|
+
- Read the Codebase Patterns section in progress.txt before starting
|
|
136
|
+
|
|
137
|
+
## When an iteration completes
|
|
138
|
+
|
|
139
|
+
Use all the key learnings from the project.txt and update the Agends.md file with those learning.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Flyway configuration file
|
|
2
|
+
# Database connection from .env file
|
|
3
|
+
flyway.url=${FLYWAY_URL}
|
|
4
|
+
flyway.user=${FLYWAY_USER}
|
|
5
|
+
flyway.password=${FLYWAY_PASSWORD}
|
|
6
|
+
|
|
7
|
+
# Migration files location
|
|
8
|
+
flyway.locations=filesystem:./supabase/migrations
|
|
9
|
+
|
|
10
|
+
# Default schema (Flyway will create flyway_schema_history table here)
|
|
11
|
+
flyway.schemas=public
|
|
12
|
+
|
|
13
|
+
# Placeholder replacement
|
|
14
|
+
flyway.placeholderReplacement=false
|
|
15
|
+
|
|
16
|
+
# Validate on migrate
|
|
17
|
+
flyway.validateOnMigrate=true
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "project",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"flyway:migrate": "dotenv -e .env -- flyway -baselineOnMigrate=true migrate",
|
|
7
|
+
"dev": "node --env-file=.env --require ts-node/register server.ts",
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"start": "NODE_ENV=production node --env-file=.env --require ts-node/register server.ts",
|
|
10
|
+
"test": "tsx --test 'src/**/*.test.ts'"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@event-driven-io/emmett": "^0.42.1-alpha.1",
|
|
14
|
+
"@event-driven-io/emmett-expressjs": "^0.42.1-alpha.1",
|
|
15
|
+
"@event-driven-io/emmett-postgresql": "^0.42.1-alpha.1",
|
|
16
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
17
|
+
"@resvg/resvg-js": "^2.6.2",
|
|
18
|
+
"@supabase/ssr": "^0.10.0",
|
|
19
|
+
"@supabase/supabase-js": "^2.100.1",
|
|
20
|
+
"cookie-parser": "^1.4.7",
|
|
21
|
+
"cors": "^2.8.6",
|
|
22
|
+
"express": "^4.18.2",
|
|
23
|
+
"glob": "^11.0.3",
|
|
24
|
+
"isomorphic-git": "^1.37.6",
|
|
25
|
+
"jose": "^6.2.3",
|
|
26
|
+
"knex": "^3.1.0",
|
|
27
|
+
"multer": "^2.1.1",
|
|
28
|
+
"node-cron": "^4.2.1",
|
|
29
|
+
"pg": "^8.17.2",
|
|
30
|
+
"sharp": "^0.34.5",
|
|
31
|
+
"simple-git": "^3.36.0",
|
|
32
|
+
"swagger-jsdoc": "^6.2.8",
|
|
33
|
+
"swagger-ui-express": "^5.0.1",
|
|
34
|
+
"url": "^0.11.4"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@eslint/eslintrc": "^3",
|
|
38
|
+
"@testcontainers/postgresql": "^11.0.3",
|
|
39
|
+
"@types/cors": "^2.8.19",
|
|
40
|
+
"@types/express": "^4.17.21",
|
|
41
|
+
"@types/multer": "^2.1.0",
|
|
42
|
+
"@types/node": "^20",
|
|
43
|
+
"@types/swagger-jsdoc": "^6.0.4",
|
|
44
|
+
"@types/swagger-ui-express": "^4.1.8",
|
|
45
|
+
"dotenv-cli": "^11.0.0",
|
|
46
|
+
"eslint": "^9",
|
|
47
|
+
"sql-formatter": "^15.7.0",
|
|
48
|
+
"ts-node": "^10.9.2",
|
|
49
|
+
"tsx": "^4.20.3",
|
|
50
|
+
"typescript": "^5"
|
|
51
|
+
}
|
|
52
|
+
}
|
package/templates/root/ralph.sh
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
#
|
|
2
|
+
# Ralph agent loop — two independent phases, each triggered by their own condition
|
|
3
|
+
#
|
|
4
|
+
# Phase 1: tasks.json has entries → load slice from board, update .slices/
|
|
5
|
+
# Phase 2: .slices/**/index.json has a "Planned" slice → build it
|
|
6
|
+
#
|
|
7
|
+
# The phases are NOT causally linked — either can trigger on its own.
|
|
8
|
+
#
|
|
3
9
|
# Usage: ./ralph.sh [project_dir]
|
|
4
|
-
# project_dir defaults to the directory containing this script (the project root)
|
|
5
10
|
|
|
6
11
|
set -euo pipefail
|
|
7
12
|
|
|
@@ -9,39 +14,55 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
9
14
|
PROJECT_DIR="${1:-"$SCRIPT_DIR"}"
|
|
10
15
|
TASKS_FILE="$PROJECT_DIR/tasks.json"
|
|
11
16
|
PROMPT_FILE="$PROJECT_DIR/prompt.md"
|
|
12
|
-
|
|
17
|
+
BACKEND_PROMPT_FILE="$PROJECT_DIR/backend-prompt.md"
|
|
18
|
+
AGENT_SCRIPT="$PROJECT_DIR/agent.sh"
|
|
13
19
|
|
|
14
20
|
if [[ ! -f "$PROJECT_DIR/.eventmodelers/config.json" ]]; then
|
|
15
21
|
echo "ERROR: No .eventmodelers/config.json found in $PROJECT_DIR"
|
|
16
22
|
exit 1
|
|
17
23
|
fi
|
|
18
24
|
|
|
19
|
-
|
|
20
|
-
echo "ERROR: No model.md found in $PROJECT_DIR"
|
|
21
|
-
exit 1
|
|
22
|
-
fi
|
|
25
|
+
echo "Ralph — project: $PROJECT_DIR"
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
# Returns 0 if tasks.json has at least one task
|
|
28
|
+
has_pending_tasks() {
|
|
29
|
+
[[ -f "$TASKS_FILE" ]] || return 1
|
|
30
|
+
local content
|
|
31
|
+
content=$(cat "$TASKS_FILE")
|
|
32
|
+
[[ "$content" != "[]" && -n "$content" ]]
|
|
33
|
+
}
|
|
25
34
|
|
|
26
|
-
|
|
27
|
-
|
|
35
|
+
# Returns 0 if any index.json under .slices/ contains a "Planned" slice
|
|
36
|
+
has_planned_slices() {
|
|
37
|
+
grep -rqi '"status"[[:space:]]*:[[:space:]]*"planned"' "$PROJECT_DIR/.slices/" 2>/dev/null
|
|
38
|
+
}
|
|
28
39
|
|
|
29
|
-
#
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
# ---- Run Claude in the project root --------------------
|
|
40
|
+
# Runs agent.sh with the given prompt; retries on non-zero exit
|
|
41
|
+
run_agent() {
|
|
42
|
+
local label="$1"
|
|
43
|
+
local prompt="$2"
|
|
34
44
|
while true; do
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
else
|
|
40
|
-
echo
|
|
41
|
-
echo "Claude exited with an error. Waiting 1 minute before retry..."
|
|
42
|
-
sleep 60
|
|
43
|
-
fi
|
|
45
|
+
echo "[$(date -u +%H:%M:%S)] $label"
|
|
46
|
+
(cd "$PROJECT_DIR" && bash "$AGENT_SCRIPT" "$prompt") 2>&1 && return 0
|
|
47
|
+
echo "[$(date -u +%H:%M:%S)] Agent error — retrying in 60s..."
|
|
48
|
+
sleep 60
|
|
44
49
|
done
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
while true; do
|
|
53
|
+
ran_something=false
|
|
54
|
+
|
|
55
|
+
if has_pending_tasks; then
|
|
56
|
+
run_agent "Phase 1: loading slice from board..." "$(cat "$PROMPT_FILE")"
|
|
57
|
+
ran_something=true
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
if has_planned_slices; then
|
|
61
|
+
run_agent "Phase 2: building slice..." "$(cat "$BACKEND_PROMPT_FILE")"
|
|
62
|
+
ran_something=true
|
|
63
|
+
fi
|
|
45
64
|
|
|
46
|
-
|
|
47
|
-
|
|
65
|
+
if [[ "$ran_something" == false ]]; then
|
|
66
|
+
sleep 3
|
|
67
|
+
fi
|
|
68
|
+
done
|