@eventmodelers/cli 0.0.6 → 0.0.8
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/README.md +42 -9
- package/cli.js +633 -175
- package/package.json +1 -1
- package/{stacks/node/templates → shared}/build-kit/README.md +3 -3
- package/{stacks/axon/templates → shared}/build-kit/lib/ralph.js +10 -3
- package/shared/build-kit/ralph-ollama.js +2 -2
- package/{stacks/node/templates → shared}/build-kit/ralph.sh +1 -1
- package/shared/build-kit/realtime-agent.js +1 -1
- package/{stacks/supabase/templates/.claude → shared}/skills/connect/SKILL.md +10 -5
- package/stacks/cratis-csharp/templates/build-kit/lib/AGENT.md +1 -0
- package/stacks/modeling-kit/templates/kit/README.md +79 -0
- package/stacks/modeling-kit/templates/kit/lib/ralph.js +9 -2
- package/stacks/modeling-kit/templates/kit/ralph.sh +1 -1
- package/stacks/node/templates/build-kit/lib/backend-prompt.md +1 -1
- package/stacks/supabase/templates/build-kit/lib/backend-prompt.md +1 -1
- package/stacks/axon/templates/.claude/skills/connect/SKILL.md +0 -178
- package/stacks/axon/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +0 -611
- package/stacks/axon/templates/.claude/skills/update-slice-status/SKILL.md +0 -105
- package/stacks/axon/templates/build-kit/ralph.sh +0 -98
- package/stacks/cratis-csharp/templates/.claude/skills/connect/SKILL.md +0 -178
- package/stacks/cratis-csharp/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +0 -609
- package/stacks/cratis-csharp/templates/.claude/skills/update-slice-status/SKILL.md +0 -105
- package/stacks/cratis-csharp/templates/build-kit/lib/agent.sh +0 -20
- package/stacks/cratis-csharp/templates/build-kit/lib/ralph.js +0 -302
- package/stacks/cratis-csharp/templates/build-kit/ralph-claude.js +0 -37
- package/stacks/cratis-csharp/templates/build-kit/ralph.sh +0 -98
- package/stacks/modeling-kit/templates/.claude/skills/connect/SKILL.md +0 -178
- package/stacks/modeling-kit/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +0 -441
- package/stacks/modeling-kit/templates/.claude/skills/update-slice-status/SKILL.md +0 -110
- package/stacks/modeling-kit/templates/kit/lib/agent.sh +0 -20
- package/stacks/modeling-kit/templates/kit/lib/ollama-agent.js +0 -147
- package/stacks/modeling-kit/templates/kit/ralph-ollama.js +0 -38
- package/stacks/modeling-kit/templates/kit/realtime-agent.js +0 -18
- package/stacks/node/templates/.claude/skills/connect/SKILL.md +0 -178
- package/stacks/node/templates/build-kit/lib/agent.sh +0 -20
- package/stacks/node/templates/build-kit/lib/ralph.js +0 -369
- package/stacks/node/templates/build-kit/ralph-claude.js +0 -44
- package/stacks/supabase/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +0 -628
- package/stacks/supabase/templates/.claude/skills/update-slice-status/SKILL.md +0 -110
- package/stacks/supabase/templates/build-kit/README.md +0 -86
- package/stacks/supabase/templates/build-kit/lib/agent.sh +0 -20
- package/stacks/supabase/templates/build-kit/lib/ralph.js +0 -369
- package/stacks/supabase/templates/build-kit/ralph-claude.js +0 -44
- package/stacks/supabase/templates/build-kit/ralph.sh +0 -98
- /package/{stacks/axon/templates → shared}/build-kit/lib/agent.sh +0 -0
- /package/{stacks/axon/templates → shared}/build-kit/ralph-claude.js +0 -0
- /package/{stacks/node/templates/.claude → shared}/skills/learn-eventmodelers-api/SKILL.md +0 -0
- /package/{stacks/node/templates/.claude → shared}/skills/update-slice-status/SKILL.md +0 -0
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@ Ralph's runtime directory. Contains the agent loop, realtime subscription, promp
|
|
|
9
9
|
node .build-kit/ralph-claude.js
|
|
10
10
|
|
|
11
11
|
# Local Ollama model — run `ollama serve` first
|
|
12
|
-
OLLAMA_MODEL=qwen3:
|
|
12
|
+
OLLAMA_MODEL=qwen3.5:9b node .build-kit/ralph-ollama.js
|
|
13
13
|
|
|
14
14
|
# Custom project directory (defaults to the parent of .build-kit)
|
|
15
15
|
node .build-kit/ralph-claude.js /path/to/project
|
|
@@ -66,13 +66,13 @@ node .build-kit/realtime-agent.js
|
|
|
66
66
|
## Ollama configuration
|
|
67
67
|
|
|
68
68
|
```bash
|
|
69
|
-
OLLAMA_MODEL=qwen3:
|
|
69
|
+
OLLAMA_MODEL=qwen3.5:9b # model to use (default: qwen3.5:9b)
|
|
70
70
|
OLLAMA_URL=http://host:11434 # Ollama server URL (default: http://localhost:11434)
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
## Config
|
|
74
74
|
|
|
75
|
-
Credentials are stored in `.build-kit/.eventmodelers/config.json` (written by `
|
|
75
|
+
Credentials are stored in `.build-kit/.eventmodelers/config.json` (written by `eventmodelers init`):
|
|
76
76
|
|
|
77
77
|
```json
|
|
78
78
|
{
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { createClient } from '@supabase/supabase-js';
|
|
9
9
|
import { readFileSync, mkdirSync, writeFileSync, existsSync, readdirSync } from 'fs';
|
|
10
10
|
import { join, dirname } from 'path';
|
|
11
|
+
import { homedir } from 'os';
|
|
11
12
|
import { randomUUID } from 'crypto';
|
|
12
13
|
|
|
13
14
|
// ── HTTP helpers ──────────────────────────────────────────────────────────────
|
|
@@ -57,9 +58,14 @@ function* configCandidates(kitDir) {
|
|
|
57
58
|
while (true) {
|
|
58
59
|
yield join(dir, '.eventmodelers', 'config.json');
|
|
59
60
|
const parent = dirname(dir);
|
|
60
|
-
if (parent === dir)
|
|
61
|
+
if (parent === dir) break;
|
|
61
62
|
dir = parent;
|
|
62
63
|
}
|
|
64
|
+
// Last resort: the walk above only passes through $HOME if the project happens
|
|
65
|
+
// to live under it. A project outside $HOME (e.g. /tmp/foo) never sees it, so
|
|
66
|
+
// check it explicitly — this is where `eventmodelers init-config --global` writes
|
|
67
|
+
// account-wide defaults (organizationId/token) shared across every project.
|
|
68
|
+
yield join(homedir(), '.eventmodelers', 'config.json');
|
|
63
69
|
}
|
|
64
70
|
|
|
65
71
|
function loadLocalConfig(kitDir) {
|
|
@@ -67,7 +73,7 @@ function loadLocalConfig(kitDir) {
|
|
|
67
73
|
const sources = [];
|
|
68
74
|
|
|
69
75
|
for (const candidate of configCandidates(kitDir)) {
|
|
70
|
-
if (!existsSync(candidate)) continue;
|
|
76
|
+
if (sources.includes(candidate) || !existsSync(candidate)) continue;
|
|
71
77
|
let cfg;
|
|
72
78
|
try {
|
|
73
79
|
cfg = JSON.parse(readFileSync(candidate, 'utf-8'));
|
|
@@ -83,6 +89,7 @@ function loadLocalConfig(kitDir) {
|
|
|
83
89
|
}
|
|
84
90
|
|
|
85
91
|
if (process.env.BASE_URL) merged.baseUrl = process.env.BASE_URL;
|
|
92
|
+
else if (!merged.baseUrl) merged.baseUrl = 'https://api.eventmodelers.ai';
|
|
86
93
|
|
|
87
94
|
if (sources.length > 1) {
|
|
88
95
|
console.log(`[ralph] Merged config from: ${sources.join(', ')}`);
|
|
@@ -90,7 +97,7 @@ function loadLocalConfig(kitDir) {
|
|
|
90
97
|
console.log(`[ralph] Using credentials from ${sources[0]}`);
|
|
91
98
|
} else if (sources.length === 0) {
|
|
92
99
|
console.warn(`[ralph] Note: no .eventmodelers/config.json found — platform sync disabled.`);
|
|
93
|
-
console.warn(` To enable board sync, follow: https://app.eventmodelers.ai/documentation#build
|
|
100
|
+
console.warn(` To enable board sync, follow: https://app.eventmodelers.ai/documentation#build`);
|
|
94
101
|
console.warn(` Code generation from local slice definitions will still run.`);
|
|
95
102
|
}
|
|
96
103
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Ralph loop + realtime agent using a local Ollama model as the executor.
|
|
3
3
|
// Run `ollama serve` first.
|
|
4
4
|
// Usage: node ralph-ollama.js [project_dir]
|
|
5
|
-
// OLLAMA_MODEL=qwen3:
|
|
5
|
+
// OLLAMA_MODEL=qwen3.5:9b node ralph-ollama.js
|
|
6
6
|
// OLLAMA_URL=http://host:11434 node ralph-ollama.js
|
|
7
7
|
|
|
8
8
|
import { startRalph } from './lib/ralph.js';
|
|
@@ -12,7 +12,7 @@ import { fileURLToPath } from 'url';
|
|
|
12
12
|
|
|
13
13
|
const kitDir = dirname(fileURLToPath(import.meta.url));
|
|
14
14
|
const projectDir = process.argv[2] ? resolve(process.argv[2]) : resolve(kitDir, '..');
|
|
15
|
-
const model = process.env.OLLAMA_MODEL || 'qwen3:
|
|
15
|
+
const model = process.env.OLLAMA_MODEL || 'qwen3.5:9b';
|
|
16
16
|
|
|
17
17
|
console.log(`[ralph-ollama] model=${model}`);
|
|
18
18
|
|
|
@@ -23,7 +23,7 @@ AGENT_SCRIPT="$KIT_DIR/lib/agent.sh"
|
|
|
23
23
|
HAS_CREDENTIALS=true
|
|
24
24
|
if [[ ! -f "$KIT_DIR/.eventmodelers/config.json" ]]; then
|
|
25
25
|
echo "[ralph] Note: no .eventmodelers/config.json found — platform sync disabled." >&2
|
|
26
|
-
echo " To enable board sync, follow: https://app.eventmodelers.ai/documentation#build
|
|
26
|
+
echo " To enable board sync, follow: https://app.eventmodelers.ai/documentation#build" >&2
|
|
27
27
|
echo " Code generation from local slice definitions will still run." >&2
|
|
28
28
|
HAS_CREDENTIALS=false
|
|
29
29
|
fi
|
|
@@ -13,6 +13,6 @@ const kitDir = process.argv[2] ? resolve(process.argv[2]) : dirname(fileURLToPat
|
|
|
13
13
|
const local = loadLocalConfig(kitDir);
|
|
14
14
|
const cfg = await retryOn401('fetchPlatformConfig', () => fetchPlatformConfig(local));
|
|
15
15
|
|
|
16
|
-
console.log(`[agent] Starting — org=${cfg.organizationId}
|
|
16
|
+
console.log(`[agent] Starting — org=${cfg.organizationId}${cfg.boardId ? `, board=${cfg.boardId}` : ''}, base=${cfg.baseUrl}`);
|
|
17
17
|
|
|
18
18
|
await startRealtimeAgent(cfg, kitDir);
|
|
@@ -48,7 +48,7 @@ If an inline `board=<uuid>` is found, use it as `BOARD_ID` — **it takes priori
|
|
|
48
48
|
|
|
49
49
|
## Step 1 — Read config file
|
|
50
50
|
|
|
51
|
-
Search for `.eventmodelers/config.json` starting from the current working directory and walking up through all parent directories:
|
|
51
|
+
Search for `.eventmodelers/config.json` starting from the current working directory and walking up through all parent directories. This is the same file every kit installed in this project reads and writes, so credentials only need to be entered once per project:
|
|
52
52
|
|
|
53
53
|
```bash
|
|
54
54
|
dir="$PWD"
|
|
@@ -66,7 +66,7 @@ done
|
|
|
66
66
|
If a file is found (at any level), note its path and extract any values **not already set by Step 0**:
|
|
67
67
|
- `token` → `TOKEN`
|
|
68
68
|
- `boardId` → `BOARD_ID`
|
|
69
|
-
- `organizationId` → `ORG_ID`
|
|
69
|
+
- `organizationId` (or `orgId`) → `ORG_ID`
|
|
70
70
|
- `baseUrl` → `BASE_URL` (default: `https://api.eventmodelers.ai` if missing)
|
|
71
71
|
|
|
72
72
|
Resolution priority: **inline param > config file > ask user**
|
|
@@ -86,7 +86,12 @@ Stop asking questions. Show this hint and wait for them to paste:
|
|
|
86
86
|
|
|
87
87
|
> "Great — please paste your config from https://app.eventmodelers.ai/account here."
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
The paste may be either a JSON object, or a single comma-separated line of `key=value` pairs (as copied out of a URL query string), in any order, e.g.:
|
|
90
|
+
```
|
|
91
|
+
token=xxxxxxxx-...,boardId=xxxxxxxx-...,organizationId=xxxxxxxx-...,baseUrl=https://api.eventmodelers.ai
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Parse either form immediately — accept both `orgId` and `organizationId` as the organization field. For the comma-separated form, split on commas and match each piece by its `key=` prefix (`token`, `boardId`, `organizationId`/`orgId`, `baseUrl`) rather than by position — do **not** assume a fixed field order, since the order the value was copied in and the order it's pasted in are not guaranteed to match. If a value has no recognizable `key=` prefix, treat it as an error and re-ask rather than guessing which field it belongs to. Apply all values, and proceed directly to Step 3.
|
|
90
95
|
|
|
91
96
|
**If the user answers no** (or pastes only a partial config), ask for each still-missing field one at a time, in this order: `token`, then `boardId`, then `orgId`. Wait for the answer before asking the next.
|
|
92
97
|
|
|
@@ -111,7 +116,7 @@ cat > .eventmodelers/config.json << 'EOF'
|
|
|
111
116
|
{
|
|
112
117
|
"token": "<TOKEN>",
|
|
113
118
|
"boardId": "<BOARD_ID>",
|
|
114
|
-
"
|
|
119
|
+
"organizationId": "<ORG_ID>",
|
|
115
120
|
"baseUrl": "<BASE_URL>"
|
|
116
121
|
}
|
|
117
122
|
EOF
|
|
@@ -162,7 +167,7 @@ curl -s -o /dev/null -w "%{http_code}" \
|
|
|
162
167
|
{
|
|
163
168
|
"token": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
164
169
|
"boardId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
165
|
-
"
|
|
170
|
+
"organizationId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
166
171
|
"baseUrl": "http://localhost:3000"
|
|
167
172
|
}
|
|
168
173
|
```
|
|
@@ -56,3 +56,4 @@ These files are always up to date — read them directly before invoking any ski
|
|
|
56
56
|
- The `boardId` and `organizationId` from each payload provide full context — pass them to skills.
|
|
57
57
|
- Node events use `node:created`, `node:changed`, `node:deleted` — always POST to `/api/org/:orgId/boards/:boardId/nodes/events`.
|
|
58
58
|
- Slice metadata (title, status) lives on the SLICE_BORDER node under `meta.sliceStatus` and `meta.title`.
|
|
59
|
+
- `/update-slice-status` rejects moving a slice into a status it's already in — this is a concurrency guard, not a bug. It means another agent already claimed the slice. Treat it as `ALREADY_IN_STATUS`, skip that slice, and move on to the next `Planned` one instead of erroring out.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# .agent-modeling-kit
|
|
2
|
+
|
|
3
|
+
Ralph's runtime directory for modeling-only projects — skills + agent loop, no backend scaffold.
|
|
4
|
+
|
|
5
|
+
## Quick start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Claude (default)
|
|
9
|
+
node .agent-modeling-kit/ralph-claude.js
|
|
10
|
+
|
|
11
|
+
# Local Ollama model — run `ollama serve` first
|
|
12
|
+
OLLAMA_MODEL=qwen3.5:9b node .agent-modeling-kit/ralph-ollama.js
|
|
13
|
+
|
|
14
|
+
# Bash-only loop (no realtime)
|
|
15
|
+
.agent-modeling-kit/ralph.sh
|
|
16
|
+
|
|
17
|
+
# Custom project directory (defaults to the parent of .agent-modeling-kit)
|
|
18
|
+
node .agent-modeling-kit/ralph-claude.js /path/to/project
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Files
|
|
22
|
+
|
|
23
|
+
**Entry points** (top level):
|
|
24
|
+
|
|
25
|
+
| File | Purpose |
|
|
26
|
+
|------|---------|
|
|
27
|
+
| `ralph-claude.js` | Runs the task loop using Claude Code as the executor |
|
|
28
|
+
| `ralph-ollama.js` | Runs the task loop using a local Ollama model |
|
|
29
|
+
| `ralph.sh` | Shell-based loop — alternative to the JS entry points |
|
|
30
|
+
| `realtime-agent.js` | Standalone realtime agent — only needed to run it in a separate terminal |
|
|
31
|
+
|
|
32
|
+
**Internals** (`lib/`):
|
|
33
|
+
|
|
34
|
+
| File | Purpose |
|
|
35
|
+
|------|---------|
|
|
36
|
+
| `lib/ralph.js` | Shared library — realtime agent + task loop; imported by the entry points |
|
|
37
|
+
| `lib/ollama-agent.js` | Ollama executor — called by `ralph-ollama.js`, can also run manually |
|
|
38
|
+
| `lib/agent.sh` | Thin shell wrapper around `claude` — called by `ralph.sh` |
|
|
39
|
+
|
|
40
|
+
## How it works
|
|
41
|
+
|
|
42
|
+
Modeling-only mode is a single phase: whenever `tasks.json` has entries, the loop runs
|
|
43
|
+
Claude (or Ollama) against the task, following the instructions in the project root
|
|
44
|
+
`CLAUDE.md` — read `.agent-modeling-kit/tasks.json`, pick the highest-priority task, run
|
|
45
|
+
the matching skill (`/timeline`, `/place-element`, `/storyboard`, ...), then remove the
|
|
46
|
+
completed task. There is no build-a-slice-into-code phase — that's what the backend
|
|
47
|
+
stacks (`node`, `supabase`, `axon`, `cratis-csharp`) add on top of this.
|
|
48
|
+
|
|
49
|
+
## Running the realtime agent separately
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Terminal 1 — realtime agent only
|
|
53
|
+
node .agent-modeling-kit/realtime-agent.js
|
|
54
|
+
|
|
55
|
+
# Terminal 2 — loop only (poll tasks.json without the realtime subscription)
|
|
56
|
+
.agent-modeling-kit/ralph.sh
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Ollama configuration
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
OLLAMA_MODEL=qwen3.5:9b # model to use (default: qwen3.5:9b)
|
|
63
|
+
OLLAMA_URL=http://host:11434 # Ollama server URL (default: http://localhost:11434)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Config
|
|
67
|
+
|
|
68
|
+
Credentials are stored in `.agent-modeling-kit/.eventmodelers/config.json` (written by `eventmodelers init-modeling`):
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"organizationId": "...",
|
|
73
|
+
"boardId": "...",
|
|
74
|
+
"token": "...",
|
|
75
|
+
"baseUrl": "https://api.eventmodelers.ai"
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Claude skills live in `.claude/skills/` and are available inside any Claude Code session started from the project root.
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { createClient } from '@supabase/supabase-js';
|
|
8
8
|
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
9
9
|
import { join, dirname } from 'path';
|
|
10
|
+
import { homedir } from 'os';
|
|
10
11
|
import { randomUUID } from 'crypto';
|
|
11
12
|
|
|
12
13
|
// ── HTTP helpers ──────────────────────────────────────────────────────────────
|
|
@@ -56,9 +57,14 @@ function* configCandidates(kitDir) {
|
|
|
56
57
|
while (true) {
|
|
57
58
|
yield join(dir, '.eventmodelers', 'config.json');
|
|
58
59
|
const parent = dirname(dir);
|
|
59
|
-
if (parent === dir)
|
|
60
|
+
if (parent === dir) break;
|
|
60
61
|
dir = parent;
|
|
61
62
|
}
|
|
63
|
+
// Last resort: the walk above only passes through $HOME if the project happens
|
|
64
|
+
// to live under it. A project outside $HOME (e.g. /tmp/foo) never sees it, so
|
|
65
|
+
// check it explicitly — this is where `eventmodelers init-config --global` writes
|
|
66
|
+
// account-wide defaults (organizationId/token) shared across every project.
|
|
67
|
+
yield join(homedir(), '.eventmodelers', 'config.json');
|
|
62
68
|
}
|
|
63
69
|
|
|
64
70
|
function loadLocalConfig(kitDir) {
|
|
@@ -66,7 +72,7 @@ function loadLocalConfig(kitDir) {
|
|
|
66
72
|
const sources = [];
|
|
67
73
|
|
|
68
74
|
for (const candidate of configCandidates(kitDir)) {
|
|
69
|
-
if (!existsSync(candidate)) continue;
|
|
75
|
+
if (sources.includes(candidate) || !existsSync(candidate)) continue;
|
|
70
76
|
let cfg;
|
|
71
77
|
try {
|
|
72
78
|
cfg = JSON.parse(readFileSync(candidate, 'utf-8'));
|
|
@@ -82,6 +88,7 @@ function loadLocalConfig(kitDir) {
|
|
|
82
88
|
}
|
|
83
89
|
|
|
84
90
|
if (process.env.BASE_URL) merged.baseUrl = process.env.BASE_URL;
|
|
91
|
+
else if (!merged.baseUrl) merged.baseUrl = 'https://api.eventmodelers.ai';
|
|
85
92
|
|
|
86
93
|
if (sources.length > 1) {
|
|
87
94
|
console.log(`[ralph] Merged config from: ${sources.join(', ')}`);
|
|
@@ -14,7 +14,7 @@ AGENT_SCRIPT="$KIT_DIR/lib/agent.sh"
|
|
|
14
14
|
|
|
15
15
|
if [[ ! -f "$KIT_DIR/.eventmodelers/config.json" ]]; then
|
|
16
16
|
echo "ERROR: No .eventmodelers/config.json found in $KIT_DIR"
|
|
17
|
-
echo "Run: npx @eventmodelers/
|
|
17
|
+
echo "Run: npx @eventmodelers/cli init-modeling"
|
|
18
18
|
exit 1
|
|
19
19
|
fi
|
|
20
20
|
|
|
@@ -149,4 +149,4 @@ If ALL slices in the current context are Done, reply with:
|
|
|
149
149
|
|
|
150
150
|
## When an iteration completes
|
|
151
151
|
|
|
152
|
-
Use all the key learnings from the
|
|
152
|
+
Use all the key learnings from the progress.txt and update the AGENTS.md file with those learnings.
|
|
@@ -149,4 +149,4 @@ If ALL slices in the current context are Done, reply with:
|
|
|
149
149
|
|
|
150
150
|
## When an iteration completes
|
|
151
151
|
|
|
152
|
-
Use all the key learnings from the
|
|
152
|
+
Use all the key learnings from the progress.txt and update the AGENTS.md file with those learnings.
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: connect
|
|
3
|
-
description: Resolve eventmodelers connection config (token, boardId, baseUrl) from inline params or .eventmodelers/config.json — ask the user for missing values, persist them, and add the file to .gitignore. All other skills invoke this first.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Connect — Resolve Eventmodelers Config
|
|
7
|
-
|
|
8
|
-
**Every other skill invokes this skill first** before making any API calls. Do not proceed past this skill until all four values (`TOKEN`, `BOARD_ID`, `ORG_ID`, `BASE_URL`) are resolved.
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## What this skill produces
|
|
13
|
-
|
|
14
|
-
After running, the following variables are available for the rest of the session:
|
|
15
|
-
|
|
16
|
-
| Variable | Header sent to API | Description |
|
|
17
|
-
|----------|--------------------|-------------|
|
|
18
|
-
| `TOKEN` | `x-token` | API token UUID |
|
|
19
|
-
| `BOARD_ID` | `x-board-id` | Target board UUID |
|
|
20
|
-
| `ORG_ID` | — | Organization UUID (used in all board-scoped URLs) |
|
|
21
|
-
| `BASE_URL` | — | Base URL, e.g. `http://localhost:3000` |
|
|
22
|
-
|
|
23
|
-
Every API call in every skill must include these headers:
|
|
24
|
-
```
|
|
25
|
-
x-token: <TOKEN>
|
|
26
|
-
x-board-id: <BOARD_ID>
|
|
27
|
-
x-user-id: <skill-name> ← set by each skill individually
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
All board-scoped URLs follow the pattern: `<BASE_URL>/api/org/<ORG_ID>/boards/<BOARD_ID>/...`
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
## Step 0 — Check for inline parameters
|
|
35
|
-
|
|
36
|
-
Before reading the config file, scan the prompt/arguments that invoked this skill for inline overrides. Supported formats:
|
|
37
|
-
|
|
38
|
-
| Pattern | Example |
|
|
39
|
-
|---------|---------|
|
|
40
|
-
| `board=<uuid>` | `board=05cda19d-d5b8-4b51-ae88-c72f2611548a` |
|
|
41
|
-
| `token=<uuid>` | `token=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` |
|
|
42
|
-
| `org=<uuid>` | `org=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` |
|
|
43
|
-
| `baseUrl=<url>` | `baseUrl=http://localhost:3000` |
|
|
44
|
-
|
|
45
|
-
If an inline `board=<uuid>` is found, use it as `BOARD_ID` — **it takes priority over the config file**. Same for `token`, `org`, and `baseUrl`. Record which values came from inline params so they are not overwritten in Step 3.
|
|
46
|
-
|
|
47
|
-
---
|
|
48
|
-
|
|
49
|
-
## Step 1 — Read config file
|
|
50
|
-
|
|
51
|
-
Search for `.eventmodelers/config.json` starting from the current working directory and walking up through all parent directories:
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
dir="$PWD"
|
|
55
|
-
config_file=""
|
|
56
|
-
while [ "$dir" != "/" ]; do
|
|
57
|
-
if [ -f "$dir/.eventmodelers/config.json" ]; then
|
|
58
|
-
config_file="$dir/.eventmodelers/config.json"
|
|
59
|
-
break
|
|
60
|
-
fi
|
|
61
|
-
dir="$(dirname "$dir")"
|
|
62
|
-
done
|
|
63
|
-
[ -n "$config_file" ] && cat "$config_file"
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
If a file is found (at any level), note its path and extract any values **not already set by Step 0**:
|
|
67
|
-
- `token` → `TOKEN`
|
|
68
|
-
- `boardId` → `BOARD_ID`
|
|
69
|
-
- `organizationId` → `ORG_ID`
|
|
70
|
-
- `baseUrl` → `BASE_URL` (default: `https://api.eventmodelers.ai` if missing)
|
|
71
|
-
|
|
72
|
-
Resolution priority: **inline param > config file > ask user**
|
|
73
|
-
|
|
74
|
-
If all four are present (from any source), skip to **Step 4 — Verify**.
|
|
75
|
-
|
|
76
|
-
---
|
|
77
|
-
|
|
78
|
-
## Step 2 — Ask for missing values
|
|
79
|
-
|
|
80
|
-
If after Steps 0 and 1 any required field is still missing, **ask the user one question first**:
|
|
81
|
-
|
|
82
|
-
> "Do you have a config from the eventmodelers accounts page? (yes / no)"
|
|
83
|
-
|
|
84
|
-
**If the user answers yes:**
|
|
85
|
-
Stop asking questions. Show this hint and wait for them to paste:
|
|
86
|
-
|
|
87
|
-
> "Great — please paste your config from https://app.eventmodelers.ai/account here."
|
|
88
|
-
|
|
89
|
-
When they paste a JSON object, parse it immediately — accept both `orgId` and `organizationId` as the organization field — apply all values, and proceed directly to Step 3.
|
|
90
|
-
|
|
91
|
-
**If the user answers no** (or pastes only a partial config), ask for each still-missing field one at a time, in this order: `token`, then `boardId`, then `orgId`. Wait for the answer before asking the next.
|
|
92
|
-
|
|
93
|
-
| Field | What to ask |
|
|
94
|
-
|-------|-------------|
|
|
95
|
-
| `token` | "Please provide your eventmodelers API token (a UUID from your workspace settings)." |
|
|
96
|
-
| `boardId` | "Please provide the board ID you want to work with (the UUID from the board URL)." |
|
|
97
|
-
| `orgId` | "Please provide your organization ID (the UUID from your organization settings)." |
|
|
98
|
-
| `baseUrl` | Do **not** ask — default to `https://api.eventmodelers.ai` silently. |
|
|
99
|
-
|
|
100
|
-
Where to find the token: users generate API tokens in their workspace settings at the eventmodelers platform. The token is shown only once at creation time. It is a UUID and must belong to the same organization as the board.
|
|
101
|
-
|
|
102
|
-
---
|
|
103
|
-
|
|
104
|
-
## Step 3 — Persist config
|
|
105
|
-
|
|
106
|
-
Once all values are collected, write the config file. When writing, merge with any existing config — do **not** overwrite fields that were provided as inline params with values from a previous config (the inline param is the user's explicit intent for this session, but the persisted value should reflect the most recently user-supplied value):
|
|
107
|
-
|
|
108
|
-
```bash
|
|
109
|
-
mkdir -p .eventmodelers
|
|
110
|
-
cat > .eventmodelers/config.json << 'EOF'
|
|
111
|
-
{
|
|
112
|
-
"token": "<TOKEN>",
|
|
113
|
-
"boardId": "<BOARD_ID>",
|
|
114
|
-
"orgId": "<ORG_ID>",
|
|
115
|
-
"baseUrl": "<BASE_URL>"
|
|
116
|
-
}
|
|
117
|
-
EOF
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
Then ensure `.eventmodelers/config.json` is in `.gitignore`. Check whether it is already present:
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
grep -q ".eventmodelers/config.json" .gitignore 2>/dev/null || echo "MISSING"
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
If `MISSING`, append it:
|
|
127
|
-
|
|
128
|
-
```bash
|
|
129
|
-
echo ".eventmodelers/config.json" >> .gitignore
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
Tell the user: `"Config saved to .eventmodelers/config.json and added to .gitignore."`
|
|
133
|
-
|
|
134
|
-
---
|
|
135
|
-
|
|
136
|
-
## Step 4 — Verify
|
|
137
|
-
|
|
138
|
-
Confirm the token and board are valid with a lightweight call:
|
|
139
|
-
|
|
140
|
-
```bash
|
|
141
|
-
curl -s -o /dev/null -w "%{http_code}" \
|
|
142
|
-
-H "x-token: <TOKEN>" \
|
|
143
|
-
-H "x-board-id: <BOARD_ID>" \
|
|
144
|
-
-H "x-user-id: connect-skill" \
|
|
145
|
-
"<BASE_URL>/api/org/<ORG_ID>/boards/<BOARD_ID>/nodes?type=CHAPTER"
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
| Response | Action |
|
|
149
|
-
|----------|--------|
|
|
150
|
-
| `200` | Config is valid. Print one line: `"Connected — board <BOARD_ID>"` and return. |
|
|
151
|
-
| `401` | Token is invalid or missing. Tell the user and re-run from Step 2, clearing `token`. |
|
|
152
|
-
| `403` | Token organization does not match board. Tell the user to check that the token was issued for the correct workspace. Re-run from Step 2 for both fields. |
|
|
153
|
-
| `404` | Board not found. Tell the user and re-run from Step 2, clearing `boardId`. |
|
|
154
|
-
| Any other | Print the status code and raw response. Ask the user how to proceed. |
|
|
155
|
-
|
|
156
|
-
---
|
|
157
|
-
|
|
158
|
-
## Config file format
|
|
159
|
-
|
|
160
|
-
`.eventmodelers/config.json`:
|
|
161
|
-
```json
|
|
162
|
-
{
|
|
163
|
-
"token": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
164
|
-
"boardId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
165
|
-
"orgId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
166
|
-
"baseUrl": "http://localhost:3000"
|
|
167
|
-
}
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
The `token` field is a secret. It is never logged or shown after initial confirmation.
|
|
171
|
-
|
|
172
|
-
---
|
|
173
|
-
|
|
174
|
-
## Security notes
|
|
175
|
-
|
|
176
|
-
- The config file is workspace-local and gitignored — never commit it.
|
|
177
|
-
- The token grants write access to all boards in its organization — treat it like a password.
|
|
178
|
-
- If a skill receives a `401` or `403` mid-session, re-invoke this skill to refresh the config before retrying.
|