@eventmodelers/cli 1.0.162 → 2.0.1
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 +22 -27
- package/RELEASE_NOTES.md +20 -2
- package/cli.js +461 -225
- package/lib/adapters/spec-kitty-adapter.js +3 -7
- package/lib/global-agent-id.js +45 -0
- package/lib/modeling-local-ai.js +4 -2
- package/package.json +1 -1
- package/shared/build-kit/README.md +4 -19
- package/shared/build-kit/lib/agent-lock.js +75 -0
- package/shared/build-kit/lib/agent-name.js +27 -0
- package/shared/build-kit/lib/event-prune.js +58 -0
- package/shared/build-kit/lib/events-view.js +121 -0
- package/shared/build-kit/lib/local-ai-agent.js +14 -26
- package/shared/build-kit/lib/mcp-register.js +17 -1
- package/shared/build-kit/lib/prompt-journal.js +191 -0
- package/shared/build-kit/lib/prompt-replay.js +35 -0
- package/shared/build-kit/lib/ralph.js +250 -163
- package/shared/build-kit/lib/slice-curation.js +40 -0
- package/shared/build-kit/lib/slice-events.js +93 -0
- package/shared/build-kit/lib/slice-git.js +86 -0
- package/shared/build-kit/lib/slice-learnings.js +197 -0
- package/shared/build-kit/lib/slice-projection.js +101 -0
- package/shared/build-kit/lib/slice-run.js +181 -0
- package/shared/build-kit/lib/slice-state.js +182 -0
- package/shared/build-kit/lib/slice-tasks.js +35 -0
- package/shared/build-kit/ralph-claude.js +4 -2
- package/shared/build-kit/ralph-exec.js +4 -2
- package/shared/build-kit/ralph-local-ai.js +5 -3
- package/shared/skills/connect/SKILL.md +1 -5
- package/shared/skills/learn-eventmodelers-api/SKILL.md +13 -0
- package/shared/skills/request-feedback/SKILL.md +1 -2
- package/shared/skills/ui-contract/SKILL.md +5 -3
- package/stacks/axon/templates/build-kit/CLAUDE.md +3 -3
- package/stacks/axon/templates/build-kit/lib/AGENT.md +6 -7
- package/stacks/axon/templates/build-kit/lib/backend-prompt.md +20 -42
- package/stacks/axon/templates/build-kit/lib/prompt.md +18 -41
- package/stacks/blank/templates/build-kit/CLAUDE.md +3 -3
- package/stacks/blank/templates/build-kit/lib/AGENT.md +6 -7
- package/stacks/blank/templates/build-kit/lib/backend-prompt.md +20 -37
- package/stacks/blank/templates/build-kit/lib/prompt.md +19 -42
- package/stacks/classic/templates/.claude/skills/build-automation/SKILL.md +1 -1
- package/stacks/classic/templates/.claude/skills/build-state-change/SKILL.md +1 -1
- package/stacks/classic/templates/.claude/skills/build-state-view/SKILL.md +2 -2
- package/stacks/classic/templates/.claude/skills/record-evidence/SKILL.md +2 -2
- package/stacks/classic/templates/build-kit/CLAUDE.md +3 -3
- package/stacks/classic/templates/build-kit/lib/AGENT.md +6 -7
- package/stacks/classic/templates/build-kit/lib/backend-prompt.md +20 -38
- package/stacks/classic/templates/build-kit/lib/prompt.md +19 -42
- package/stacks/kurrent/templates/build-kit/CLAUDE.md +3 -4
- package/stacks/kurrent/templates/build-kit/lib/AGENT.md +6 -7
- package/stacks/kurrent/templates/build-kit/lib/backend-prompt.md +20 -42
- package/stacks/kurrent/templates/build-kit/lib/prompt.md +18 -41
- package/stacks/modeling-kit/templates/.claude/skills/attributes/SKILL.md +1 -17
- package/stacks/modeling-kit/templates/.claude/skills/detect-model-drift/SKILL.md +1 -1
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-core-rules/SKILL.md +2 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +7 -3
- package/stacks/modeling-kit/templates/.claude/skills/examples/SKILL.md +0 -8
- package/stacks/modeling-kit/templates/.claude/skills/stressor-analysis/SKILL.md +0 -2
- package/stacks/modeling-kit/templates/.claude/skills/walkthrough/SKILL.md +1 -1
- package/stacks/modeling-kit/templates/.claude/skills/wdyt/SKILL.md +1 -1
- package/stacks/modeling-kit/templates/kit/AGENTS.md +0 -1
- package/stacks/modeling-kit/templates/kit/CLAUDE-STANDALONE.md +3 -4
- package/stacks/modeling-kit/templates/kit/CLAUDE.md +11 -72
- package/stacks/modeling-kit/templates/kit/README.md +1 -1
- package/stacks/modeling-kit/templates/kit/lib/config.js +1 -1
- package/stacks/node/templates/build-kit/CLAUDE.md +3 -3
- package/stacks/node/templates/build-kit/lib/AGENT.md +6 -7
- package/stacks/node/templates/build-kit/lib/backend-prompt.md +20 -42
- package/stacks/node/templates/build-kit/lib/prompt.md +18 -41
- package/stacks/opencqrs/templates/build-kit/CLAUDE.md +3 -4
- package/stacks/opencqrs/templates/build-kit/lib/AGENT.md +6 -7
- package/stacks/opencqrs/templates/build-kit/lib/backend-prompt.md +20 -42
- package/stacks/opencqrs/templates/build-kit/lib/prompt.md +18 -41
- package/stacks/react/templates/.claude/skills/build-state-change/SKILL.md +1 -1
- package/stacks/react/templates/.claude/skills/build-state-view/SKILL.md +1 -1
- package/stacks/react/templates/build-kit/CLAUDE.md +3 -3
- package/stacks/react/templates/build-kit/lib/AGENT.md +6 -7
- package/stacks/react/templates/build-kit/lib/backend-prompt.md +20 -37
- package/stacks/react/templates/build-kit/lib/prompt.md +19 -42
- package/stacks/supabase/templates/build-kit/CLAUDE.md +3 -3
- package/stacks/supabase/templates/build-kit/lib/AGENT.md +6 -7
- package/stacks/supabase/templates/build-kit/lib/backend-prompt.md +20 -42
- package/stacks/supabase/templates/build-kit/lib/prompt.md +18 -41
- package/stacks/supabase-react/templates/.claude/skills/build-state-change/SKILL.md +2 -2
- package/stacks/supabase-react/templates/.claude/skills/build-state-view/SKILL.md +2 -2
- package/stacks/supabase-react/templates/build-kit/CLAUDE.md +3 -3
- package/stacks/supabase-react/templates/build-kit/lib/AGENT.md +6 -7
- package/stacks/supabase-react/templates/build-kit/lib/backend-prompt.md +20 -37
- package/stacks/supabase-react/templates/build-kit/lib/prompt.md +19 -42
- package/stacks/umadb/templates/build-kit/CLAUDE.md +3 -3
- package/stacks/umadb/templates/build-kit/lib/AGENT.md +6 -7
- package/stacks/umadb/templates/build-kit/lib/backend-prompt.md +20 -42
- package/stacks/umadb/templates/build-kit/lib/prompt.md +18 -41
- package/shared/bridge/spec-kitty/bridge-spec-kitty-specify/SKILL.md +0 -125
- package/shared/bridge/spec-kitty/bridge-spec-kitty-tasks/SKILL.md +0 -95
- package/shared/build-kit/lib/agent.sh +0 -20
- package/shared/build-kit/ralph.sh +0 -111
- package/shared/build-kit/realtime-agent.js +0 -20
- package/stacks/bridge/templates/bridge/lib/AGENT.md +0 -47
- package/stacks/bridge/templates/bridge/lib/prompt.md +0 -111
- package/stacks/bridge/templates/bridge/ralph-claude.js +0 -50
- package/stacks/bridge/templates/bridge/ralph-hook.js +0 -103
- package/stacks/bridge/templates/bridge/ralph-local-ai.js +0 -43
- package/stacks/bridge/templates/bridge/ralph-static.js +0 -90
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
# Bridge Task Instructions
|
|
2
|
-
|
|
3
|
-
You are an autonomous agent reacting to slice status change events on an
|
|
4
|
-
Eventmodelers board. Unlike a build-kit agent, you do **not** write
|
|
5
|
-
application code — you translate the board's event model into artifacts for
|
|
6
|
-
another spec framework (e.g. Spec Kitty), keeping that framework's files in
|
|
7
|
-
sync with the board as it changes.
|
|
8
|
-
|
|
9
|
-
## Your Loop
|
|
10
|
-
|
|
11
|
-
1. Read `AGENT.md` to load accumulated learnings before doing anything else.
|
|
12
|
-
2. Read `.bridge-kit/tasks.json`.
|
|
13
|
-
3. If `tasks.json` is empty or missing, reply with:
|
|
14
|
-
<promise>IDLE</promise>
|
|
15
|
-
and stop.
|
|
16
|
-
4. Pick the **oldest task** (earliest `createdAt`).
|
|
17
|
-
5. Execute the task — see the Execution section below.
|
|
18
|
-
6. After execution, remove that task from the array and write
|
|
19
|
-
`.bridge-kit/tasks.json` back.
|
|
20
|
-
7. Append a progress entry to `progress.txt` (create if missing).
|
|
21
|
-
8. Update `AGENT.md` with any new reusable learnings discovered this
|
|
22
|
-
iteration.
|
|
23
|
-
9. Reply normally so the next iteration can pick up the next task.
|
|
24
|
-
|
|
25
|
-
## Execution
|
|
26
|
-
|
|
27
|
-
Each task has a single `payload` of type `SliceChangedPayload`:
|
|
28
|
-
|
|
29
|
-
```
|
|
30
|
-
{
|
|
31
|
-
event: "slice:changed"
|
|
32
|
-
organizationId: string | null
|
|
33
|
-
boardId: string
|
|
34
|
-
sliceId: string ← SLICE_BORDER node UUID
|
|
35
|
-
sliceTitle: string | null
|
|
36
|
-
sliceStatus: string | null ← e.g. "Planned", "InProgress", "Done"
|
|
37
|
-
timestamp: number
|
|
38
|
-
}
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Unlike a build-kit agent, a bridge agent reacts to **every** status change
|
|
42
|
-
(including `Planned`) — a task is queued regardless of `sliceStatus`. There
|
|
43
|
-
is no separate "claim and build" step, so don't invoke `/update-slice-status`
|
|
44
|
-
here; translation doesn't take ownership of a slice the way building does.
|
|
45
|
-
|
|
46
|
-
### Step 1 — Load credentials
|
|
47
|
-
|
|
48
|
-
Run `/connect` to resolve `TOKEN`, `BOARD_ID`, `ORG_ID`, and `BASE_URL` from
|
|
49
|
-
`.eventmodelers/config.json`.
|
|
50
|
-
|
|
51
|
-
### Step 2 — Resolve the bridge target
|
|
52
|
-
|
|
53
|
-
Read `.bridge-kit/bridge.json`'s `target` field (e.g. `"spec-kitty"`). The
|
|
54
|
-
matching translation skill is named `bridge-<target>-specify`.
|
|
55
|
-
|
|
56
|
-
### Step 3 — Translate
|
|
57
|
-
|
|
58
|
-
Invoke the `bridge-<target>-specify` skill. It reads `.slices/` (written by
|
|
59
|
-
`npx @eventmodelers/cli listen`, which must be running separately — see that
|
|
60
|
-
skill's own guardrail if `.slices/` doesn't exist yet) and regenerates that
|
|
61
|
-
framework's spec artifacts **wholesale** from the current board state. Do
|
|
62
|
-
not hand-author or patch those artifacts yourself — if they're wrong, the
|
|
63
|
-
event model is wrong; fix it on the board and let the next slice change
|
|
64
|
-
re-trigger this skill.
|
|
65
|
-
|
|
66
|
-
Do **not** also invoke `bridge-<target>-tasks` from this loop. That
|
|
67
|
-
companion skill enforces the 1:1 slice-to-work-package mapping during that
|
|
68
|
-
framework's own task-planning phase (e.g. Spec Kitty's
|
|
69
|
-
`/spec-kitty.tasks-outline`) — it's triggered by that framework's own
|
|
70
|
-
tooling when the user reaches that phase, not by every board change.
|
|
71
|
-
|
|
72
|
-
## Updating tasks.json
|
|
73
|
-
|
|
74
|
-
After completing a task, remove it from the array and write the updated
|
|
75
|
-
array back to `.bridge-kit/tasks.json`. If the array is now empty, write
|
|
76
|
-
`[]`.
|
|
77
|
-
|
|
78
|
-
## Progress Report Format
|
|
79
|
-
|
|
80
|
-
APPEND to `progress.txt` (never replace):
|
|
81
|
-
```
|
|
82
|
-
## [ISO timestamp] — Task [task.id]
|
|
83
|
-
|
|
84
|
-
Slice: [sliceTitle] ([sliceId])
|
|
85
|
-
Status change: [sliceStatus]
|
|
86
|
-
|
|
87
|
-
Action taken:
|
|
88
|
-
- [what was translated / regenerated in response to the slice change]
|
|
89
|
-
|
|
90
|
-
Learnings:
|
|
91
|
-
- [any patterns, gotchas, or reusable knowledge discovered]
|
|
92
|
-
---
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
## Stop Condition
|
|
96
|
-
|
|
97
|
-
If `.bridge-kit/tasks.json` is empty (`[]`) or does not exist, reply with:
|
|
98
|
-
<promise>IDLE</promise>
|
|
99
|
-
|
|
100
|
-
## Updating AGENT.md
|
|
101
|
-
|
|
102
|
-
After completing a task, add any **reusable** learnings to `AGENT.md` —
|
|
103
|
-
patterns, gotchas, or skill behaviour that future iterations should know.
|
|
104
|
-
Only add things that are general and applicable beyond this single task. Do
|
|
105
|
-
not duplicate what is already there.
|
|
106
|
-
|
|
107
|
-
## Important
|
|
108
|
-
|
|
109
|
-
- Process **one task per iteration**.
|
|
110
|
-
- Read `AGENT.md` first — it contains patterns from previous iterations.
|
|
111
|
-
- Always start with `/connect` if credentials are not yet loaded.
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Bridge loop using Claude Code as the executor. Unlike build-kit's
|
|
3
|
-
// ralph-claude.js, there is no onPlannedSlice consumer — a bridge agent
|
|
4
|
-
// doesn't "build" a Planned slice, it just translates every slice change as
|
|
5
|
-
// it arrives via tasks.json (see lib/prompt.md and queueAllStatuses below).
|
|
6
|
-
// Usage: node ralph-claude.js [project_dir]
|
|
7
|
-
|
|
8
|
-
import { startRalph, loadLocalConfig } from './lib/ralph.js';
|
|
9
|
-
import { spawn } from 'child_process';
|
|
10
|
-
import { dirname, resolve } from 'path';
|
|
11
|
-
import { fileURLToPath } from 'url';
|
|
12
|
-
|
|
13
|
-
const kitDir = dirname(fileURLToPath(import.meta.url));
|
|
14
|
-
const projectDir = process.argv[2] ? resolve(process.argv[2]) : resolve(kitDir, '..');
|
|
15
|
-
|
|
16
|
-
const cfg = loadLocalConfig(kitDir);
|
|
17
|
-
const inlineHeader = cfg.boardId
|
|
18
|
-
? `board=${cfg.boardId} token=${cfg.token} org=${cfg.organizationId} baseUrl=${cfg.baseUrl}\n\n`
|
|
19
|
-
: '';
|
|
20
|
-
|
|
21
|
-
const claudeArgs = ['--dangerously-skip-permissions'];
|
|
22
|
-
if (cfg.model) claudeArgs.push('--model', cfg.model);
|
|
23
|
-
const claudeEnv = cfg.anthropicBaseUrl
|
|
24
|
-
? { ...process.env, ANTHROPIC_BASE_URL: cfg.anthropicBaseUrl }
|
|
25
|
-
: process.env;
|
|
26
|
-
|
|
27
|
-
function runClaude(prompt) {
|
|
28
|
-
return new Promise((resolve, reject) => {
|
|
29
|
-
// Prompt on stdin, not as a `-p` argument — Windows cuts a multi-line argv entry at its first newline.
|
|
30
|
-
const proc = spawn('claude', [...claudeArgs, '-p'], {
|
|
31
|
-
cwd: projectDir,
|
|
32
|
-
stdio: ['pipe', 'inherit', 'inherit'],
|
|
33
|
-
env: claudeEnv,
|
|
34
|
-
});
|
|
35
|
-
proc.stdin.end(inlineHeader + prompt);
|
|
36
|
-
proc.on('close', (code) => (code === 0 ? resolve() : reject(new Error(`Claude exited ${code}`))));
|
|
37
|
-
proc.on('error', reject);
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
startRalph({
|
|
42
|
-
kitDir,
|
|
43
|
-
projectDir,
|
|
44
|
-
onTask: runClaude,
|
|
45
|
-
agentType: 'BRIDGE',
|
|
46
|
-
queueAllStatuses: true,
|
|
47
|
-
}).catch((err) => {
|
|
48
|
-
console.error('[ralph] Fatal:', err);
|
|
49
|
-
process.exit(1);
|
|
50
|
-
});
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Bridge loop that hands each batch of slice changes to an arbitrary external
|
|
3
|
-
// command instead of an AI agent — e.g. commit + push .slices/ and let a CI
|
|
4
|
-
// pipeline take it from there. No Claude/Ollama call happens in this mode.
|
|
5
|
-
//
|
|
6
|
-
// Configure the hook with `bridge --hook "<command>"` (one-off) or persist a
|
|
7
|
-
// default with `init --bridge --target <name> --hook "<command>"`.
|
|
8
|
-
//
|
|
9
|
-
// Usage: node ralph-hook.js [project_dir]
|
|
10
|
-
// BRIDGE_HOOK_CMD="git add .slices && git commit -m sync && git push" node ralph-hook.js
|
|
11
|
-
|
|
12
|
-
import { startRalph } from './lib/ralph.js';
|
|
13
|
-
import { spawn } from 'child_process';
|
|
14
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
15
|
-
import { dirname, join, resolve } from 'path';
|
|
16
|
-
import { fileURLToPath } from 'url';
|
|
17
|
-
|
|
18
|
-
const kitDir = dirname(fileURLToPath(import.meta.url));
|
|
19
|
-
const projectDir = process.argv[2] ? resolve(process.argv[2]) : resolve(kitDir, '..');
|
|
20
|
-
|
|
21
|
-
// bridge.json (not .eventmodelers/config.json) — a plain, committed sibling
|
|
22
|
-
// file, since target/hookCommand are project policy meant to be shared with
|
|
23
|
-
// every teammate and CI runner, not gitignored per-machine state.
|
|
24
|
-
function loadBridgeConfig() {
|
|
25
|
-
const p = join(kitDir, 'bridge.json');
|
|
26
|
-
if (!existsSync(p)) return {};
|
|
27
|
-
try { return JSON.parse(readFileSync(p, 'utf-8')); } catch { return {}; }
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const hookCmd = process.env.BRIDGE_HOOK_CMD || loadBridgeConfig().hookCommand;
|
|
31
|
-
if (!hookCmd) {
|
|
32
|
-
console.error('[bridge-hook] No hook command configured.');
|
|
33
|
-
console.error(' Set one for this run: eventmodelers bridge --hook "<command>"');
|
|
34
|
-
console.error(' Or persist a default: eventmodelers init --bridge --target <name> --hook "<command>"');
|
|
35
|
-
process.exit(1);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const tasksPath = join(kitDir, 'tasks.json');
|
|
39
|
-
|
|
40
|
-
function readTasks() {
|
|
41
|
-
try {
|
|
42
|
-
return JSON.parse(readFileSync(tasksPath, 'utf-8'));
|
|
43
|
-
} catch {
|
|
44
|
-
return [];
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Batched, not one-task-at-a-time: a hook like "commit + push .slices/" acts
|
|
49
|
-
// on the whole current board export in one shot (already fresh — lib/ralph.js
|
|
50
|
-
// re-syncs .slices/ before every task is queued), not on a single slice's
|
|
51
|
-
// translation the way an AI agent does. Only the tasks present at invocation
|
|
52
|
-
// time are cleared afterward — anything the realtime agent queues *while* the
|
|
53
|
-
// hook is still running is left in place for the next tick, so a slice change
|
|
54
|
-
// arriving mid-run is never silently dropped.
|
|
55
|
-
function runHook() {
|
|
56
|
-
const batch = readTasks();
|
|
57
|
-
if (!batch.length) return Promise.resolve();
|
|
58
|
-
|
|
59
|
-
const batchFilePath = join(kitDir, 'last-hook-batch.json');
|
|
60
|
-
mkdirSync(kitDir, { recursive: true });
|
|
61
|
-
writeFileSync(batchFilePath, JSON.stringify(batch, null, 2));
|
|
62
|
-
|
|
63
|
-
const latest = batch[batch.length - 1]?.payload ?? {};
|
|
64
|
-
|
|
65
|
-
return new Promise((resolvePromise, reject) => {
|
|
66
|
-
console.log(`[bridge-hook] Running hook for ${batch.length} change(s): ${hookCmd}`);
|
|
67
|
-
const proc = spawn(hookCmd, {
|
|
68
|
-
cwd: projectDir,
|
|
69
|
-
shell: true,
|
|
70
|
-
stdio: 'inherit',
|
|
71
|
-
env: {
|
|
72
|
-
...process.env,
|
|
73
|
-
BRIDGE_BATCH_FILE: batchFilePath,
|
|
74
|
-
BRIDGE_TASK_COUNT: String(batch.length),
|
|
75
|
-
BRIDGE_SLICE_ID: latest.sliceId ?? '',
|
|
76
|
-
BRIDGE_SLICE_TITLE: latest.sliceTitle ?? '',
|
|
77
|
-
BRIDGE_SLICE_STATUS: latest.sliceStatus ?? '',
|
|
78
|
-
BRIDGE_BOARD_ID: latest.boardId ?? '',
|
|
79
|
-
BRIDGE_ORGANIZATION_ID: latest.organizationId ?? '',
|
|
80
|
-
},
|
|
81
|
-
});
|
|
82
|
-
proc.on('close', (code) => {
|
|
83
|
-
if (code !== 0) return reject(new Error(`hook exited ${code}`));
|
|
84
|
-
const handledIds = new Set(batch.map((t) => t.id));
|
|
85
|
-
const remaining = readTasks().filter((t) => !handledIds.has(t.id));
|
|
86
|
-
writeFileSync(tasksPath, JSON.stringify(remaining, null, 2), 'utf-8');
|
|
87
|
-
resolvePromise();
|
|
88
|
-
});
|
|
89
|
-
proc.on('error', reject);
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
startRalph({
|
|
94
|
-
kitDir,
|
|
95
|
-
projectDir,
|
|
96
|
-
onTask: runHook,
|
|
97
|
-
// onPlannedSlice omitted — see stacks/bridge/templates/bridge/ralph-claude.js
|
|
98
|
-
agentType: 'BRIDGE',
|
|
99
|
-
queueAllStatuses: true,
|
|
100
|
-
}).catch((err) => {
|
|
101
|
-
console.error('[ralph] Fatal:', err);
|
|
102
|
-
process.exit(1);
|
|
103
|
-
});
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Bridge loop using a local AI model as the executor. Same caveats as
|
|
3
|
-
// build-kit's ralph-local-ai.js — lib/local-ai-agent.js is shared as-is (see
|
|
4
|
-
// useShared in cli.js), unmodified for bridge.
|
|
5
|
-
// Backend is selected by dialect, not by a separate runner: Ollama (native
|
|
6
|
-
// /api/chat) or any OpenAI-compatible server (vLLM, LM Studio, llama.cpp, TGI).
|
|
7
|
-
//
|
|
8
|
-
// Usage: node ralph-local-ai.js [project_dir]
|
|
9
|
-
// LOCAL_AI_TARGET=ollama node ralph-local-ai.js # run `ollama serve` first
|
|
10
|
-
// LOCAL_AI_TARGET=vllm node ralph-local-ai.js
|
|
11
|
-
// LOCAL_AI_URL=http://gpu-box:8000/v1 LOCAL_AI_MODEL=Qwen/Qwen3-8B node ralph-local-ai.js
|
|
12
|
-
import { startRalph } from './lib/ralph.js';
|
|
13
|
-
import { spawn } from 'child_process';
|
|
14
|
-
import { dirname, join, resolve } from 'path';
|
|
15
|
-
import { fileURLToPath } from 'url';
|
|
16
|
-
|
|
17
|
-
const kitDir = dirname(fileURLToPath(import.meta.url));
|
|
18
|
-
const projectDir = process.argv[2] ? resolve(process.argv[2]) : resolve(kitDir, '..');
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
function runLocalAi() {
|
|
22
|
-
return new Promise((resolve, reject) => {
|
|
23
|
-
const proc = spawn('node', [join(kitDir, 'lib', 'local-ai-agent.js')], {
|
|
24
|
-
cwd: projectDir,
|
|
25
|
-
stdio: 'inherit',
|
|
26
|
-
env: process.env,
|
|
27
|
-
});
|
|
28
|
-
proc.on('close', (code) => (code === 0 ? resolve() : reject(new Error(`local-ai-agent exited ${code}`))));
|
|
29
|
-
proc.on('error', reject);
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
startRalph({
|
|
34
|
-
kitDir,
|
|
35
|
-
projectDir,
|
|
36
|
-
onTask: runLocalAi,
|
|
37
|
-
// onPlannedSlice omitted — local-ai-agent manages its own task queue
|
|
38
|
-
agentType: 'BRIDGE',
|
|
39
|
-
queueAllStatuses: true,
|
|
40
|
-
}).catch((err) => {
|
|
41
|
-
console.error('[ralph] Fatal:', err);
|
|
42
|
-
process.exit(1);
|
|
43
|
-
});
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Bridge loop that hands each batch of slice changes to a deterministic JS
|
|
3
|
-
// adapter instead of an AI agent or an arbitrary shell hook — no LLM call
|
|
4
|
-
// happens in this mode. Which adapter runs is picked by bridge.json's target
|
|
5
|
-
// (adapters/<target>-adapter.js); if the installed target has no adapter file
|
|
6
|
-
// yet, this exits with a clear error rather than silently doing nothing.
|
|
7
|
-
//
|
|
8
|
-
// Usage: node ralph-static.js [project_dir]
|
|
9
|
-
|
|
10
|
-
import { startRalph, loadLocalConfig, fetchPlatformConfig } from './lib/ralph.js';
|
|
11
|
-
import { existsSync, readFileSync } from 'fs';
|
|
12
|
-
import { dirname, join, resolve } from 'path';
|
|
13
|
-
import { fileURLToPath, pathToFileURL } from 'url';
|
|
14
|
-
|
|
15
|
-
const kitDir = dirname(fileURLToPath(import.meta.url));
|
|
16
|
-
const projectDir = process.argv[2] ? resolve(process.argv[2]) : resolve(kitDir, '..');
|
|
17
|
-
|
|
18
|
-
function loadBridgeConfig() {
|
|
19
|
-
const p = join(kitDir, 'bridge.json');
|
|
20
|
-
if (!existsSync(p)) return {};
|
|
21
|
-
try {
|
|
22
|
-
return JSON.parse(readFileSync(p, 'utf-8'));
|
|
23
|
-
} catch {
|
|
24
|
-
return {};
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const target = loadBridgeConfig().target;
|
|
29
|
-
if (!target) {
|
|
30
|
-
console.error('[bridge-static] No target configured in bridge.json — run `eventmodelers init --bridge --target <name>` first.');
|
|
31
|
-
process.exit(1);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const adapterPath = join(kitDir, 'adapters', `${target}-adapter.js`);
|
|
35
|
-
if (!existsSync(adapterPath)) {
|
|
36
|
-
console.error(`[bridge-static] No static adapter for target "${target}" (expected ${adapterPath}).`);
|
|
37
|
-
console.error(' Use the default Claude runner for this target instead: `eventmodelers bridge`.');
|
|
38
|
-
process.exit(1);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const local = loadLocalConfig(kitDir);
|
|
42
|
-
|
|
43
|
-
// The adapter itself doesn't know about the bridge loop's .slices/ convention
|
|
44
|
-
// (fetch --spec-kitty calls the same run() with a context name of its own) —
|
|
45
|
-
// this is the one place that convention still applies, so resolve it here.
|
|
46
|
-
function readCurrentContext() {
|
|
47
|
-
const ctxPath = join(kitDir, '.slices', 'current_context.json');
|
|
48
|
-
if (!existsSync(ctxPath)) return null;
|
|
49
|
-
try {
|
|
50
|
-
return JSON.parse(readFileSync(ctxPath, 'utf-8')).name || null;
|
|
51
|
-
} catch {
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async function runAdapter() {
|
|
57
|
-
const contextName = readCurrentContext();
|
|
58
|
-
if (!contextName) {
|
|
59
|
-
console.log('[bridge-static] No .slices/current_context.json yet — waiting for the first board sync.');
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const { run } = await import(pathToFileURL(adapterPath).href);
|
|
64
|
-
const cfg = await fetchPlatformConfig(local);
|
|
65
|
-
try {
|
|
66
|
-
await run({ cfg, projectDir, contextName });
|
|
67
|
-
} catch (err) {
|
|
68
|
-
// lib/ralph.js retries any onTask failure after 60s forever — right for
|
|
69
|
-
// transient errors (network blips, a slow spec-kitty command), wrong for
|
|
70
|
-
// a missing one-time setup step that retrying can never fix on its own.
|
|
71
|
-
// Adapters flag those with `err.fatal` so we stop instead of looping.
|
|
72
|
-
if (err.fatal) {
|
|
73
|
-
console.error(`[bridge-static] ${err.message}`);
|
|
74
|
-
process.exit(1);
|
|
75
|
-
}
|
|
76
|
-
throw err;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
startRalph({
|
|
81
|
-
kitDir,
|
|
82
|
-
projectDir,
|
|
83
|
-
onTask: runAdapter,
|
|
84
|
-
// onPlannedSlice omitted — see ralph-claude.js in this same directory.
|
|
85
|
-
agentType: 'BRIDGE',
|
|
86
|
-
queueAllStatuses: true,
|
|
87
|
-
}).catch((err) => {
|
|
88
|
-
console.error('[ralph] Fatal:', err);
|
|
89
|
-
process.exit(1);
|
|
90
|
-
});
|