@eventmodelers/cli 1.0.163 → 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.
Files changed (98) hide show
  1. package/README.md +22 -27
  2. package/RELEASE_NOTES.md +20 -2
  3. package/cli.js +461 -225
  4. package/lib/adapters/spec-kitty-adapter.js +3 -7
  5. package/lib/global-agent-id.js +45 -0
  6. package/lib/modeling-local-ai.js +4 -2
  7. package/package.json +1 -1
  8. package/shared/build-kit/README.md +4 -19
  9. package/shared/build-kit/lib/agent-lock.js +75 -0
  10. package/shared/build-kit/lib/agent-name.js +27 -0
  11. package/shared/build-kit/lib/event-prune.js +58 -0
  12. package/shared/build-kit/lib/events-view.js +121 -0
  13. package/shared/build-kit/lib/local-ai-agent.js +14 -26
  14. package/shared/build-kit/lib/mcp-register.js +17 -1
  15. package/shared/build-kit/lib/prompt-journal.js +191 -0
  16. package/shared/build-kit/lib/prompt-replay.js +35 -0
  17. package/shared/build-kit/lib/ralph.js +201 -156
  18. package/shared/build-kit/lib/slice-curation.js +40 -0
  19. package/shared/build-kit/lib/slice-events.js +93 -0
  20. package/shared/build-kit/lib/slice-git.js +86 -0
  21. package/shared/build-kit/lib/slice-learnings.js +197 -0
  22. package/shared/build-kit/lib/slice-projection.js +101 -0
  23. package/shared/build-kit/lib/slice-run.js +181 -0
  24. package/shared/build-kit/lib/slice-state.js +182 -0
  25. package/shared/build-kit/lib/slice-tasks.js +35 -0
  26. package/shared/build-kit/ralph-claude.js +4 -2
  27. package/shared/build-kit/ralph-exec.js +4 -2
  28. package/shared/build-kit/ralph-local-ai.js +5 -3
  29. package/shared/skills/learn-eventmodelers-api/SKILL.md +13 -0
  30. package/shared/skills/request-feedback/SKILL.md +1 -2
  31. package/shared/skills/ui-contract/SKILL.md +5 -3
  32. package/stacks/axon/templates/build-kit/CLAUDE.md +3 -3
  33. package/stacks/axon/templates/build-kit/lib/AGENT.md +6 -7
  34. package/stacks/axon/templates/build-kit/lib/backend-prompt.md +20 -42
  35. package/stacks/axon/templates/build-kit/lib/prompt.md +18 -41
  36. package/stacks/blank/templates/build-kit/CLAUDE.md +3 -3
  37. package/stacks/blank/templates/build-kit/lib/AGENT.md +6 -7
  38. package/stacks/blank/templates/build-kit/lib/backend-prompt.md +20 -37
  39. package/stacks/blank/templates/build-kit/lib/prompt.md +19 -42
  40. package/stacks/classic/templates/.claude/skills/build-automation/SKILL.md +1 -1
  41. package/stacks/classic/templates/.claude/skills/build-state-change/SKILL.md +1 -1
  42. package/stacks/classic/templates/.claude/skills/build-state-view/SKILL.md +2 -2
  43. package/stacks/classic/templates/.claude/skills/record-evidence/SKILL.md +2 -2
  44. package/stacks/classic/templates/build-kit/CLAUDE.md +3 -3
  45. package/stacks/classic/templates/build-kit/lib/AGENT.md +6 -7
  46. package/stacks/classic/templates/build-kit/lib/backend-prompt.md +20 -38
  47. package/stacks/classic/templates/build-kit/lib/prompt.md +19 -42
  48. package/stacks/kurrent/templates/build-kit/CLAUDE.md +3 -4
  49. package/stacks/kurrent/templates/build-kit/lib/AGENT.md +6 -7
  50. package/stacks/kurrent/templates/build-kit/lib/backend-prompt.md +20 -42
  51. package/stacks/kurrent/templates/build-kit/lib/prompt.md +18 -41
  52. package/stacks/modeling-kit/templates/.claude/skills/detect-model-drift/SKILL.md +1 -1
  53. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-core-rules/SKILL.md +2 -0
  54. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +7 -3
  55. package/stacks/modeling-kit/templates/.claude/skills/wdyt/SKILL.md +1 -1
  56. package/stacks/modeling-kit/templates/kit/CLAUDE-STANDALONE.md +3 -4
  57. package/stacks/modeling-kit/templates/kit/CLAUDE.md +9 -24
  58. package/stacks/modeling-kit/templates/kit/README.md +1 -1
  59. package/stacks/modeling-kit/templates/kit/lib/config.js +1 -1
  60. package/stacks/node/templates/build-kit/CLAUDE.md +3 -3
  61. package/stacks/node/templates/build-kit/lib/AGENT.md +6 -7
  62. package/stacks/node/templates/build-kit/lib/backend-prompt.md +20 -42
  63. package/stacks/node/templates/build-kit/lib/prompt.md +18 -41
  64. package/stacks/opencqrs/templates/build-kit/CLAUDE.md +3 -4
  65. package/stacks/opencqrs/templates/build-kit/lib/AGENT.md +6 -7
  66. package/stacks/opencqrs/templates/build-kit/lib/backend-prompt.md +20 -42
  67. package/stacks/opencqrs/templates/build-kit/lib/prompt.md +18 -41
  68. package/stacks/react/templates/.claude/skills/build-state-change/SKILL.md +1 -1
  69. package/stacks/react/templates/.claude/skills/build-state-view/SKILL.md +1 -1
  70. package/stacks/react/templates/build-kit/CLAUDE.md +3 -3
  71. package/stacks/react/templates/build-kit/lib/AGENT.md +6 -7
  72. package/stacks/react/templates/build-kit/lib/backend-prompt.md +20 -37
  73. package/stacks/react/templates/build-kit/lib/prompt.md +19 -42
  74. package/stacks/supabase/templates/build-kit/CLAUDE.md +3 -3
  75. package/stacks/supabase/templates/build-kit/lib/AGENT.md +6 -7
  76. package/stacks/supabase/templates/build-kit/lib/backend-prompt.md +20 -42
  77. package/stacks/supabase/templates/build-kit/lib/prompt.md +18 -41
  78. package/stacks/supabase-react/templates/.claude/skills/build-state-change/SKILL.md +2 -2
  79. package/stacks/supabase-react/templates/.claude/skills/build-state-view/SKILL.md +2 -2
  80. package/stacks/supabase-react/templates/build-kit/CLAUDE.md +3 -3
  81. package/stacks/supabase-react/templates/build-kit/lib/AGENT.md +6 -7
  82. package/stacks/supabase-react/templates/build-kit/lib/backend-prompt.md +20 -37
  83. package/stacks/supabase-react/templates/build-kit/lib/prompt.md +19 -42
  84. package/stacks/umadb/templates/build-kit/CLAUDE.md +3 -3
  85. package/stacks/umadb/templates/build-kit/lib/AGENT.md +6 -7
  86. package/stacks/umadb/templates/build-kit/lib/backend-prompt.md +20 -42
  87. package/stacks/umadb/templates/build-kit/lib/prompt.md +18 -41
  88. package/shared/bridge/spec-kitty/bridge-spec-kitty-specify/SKILL.md +0 -125
  89. package/shared/bridge/spec-kitty/bridge-spec-kitty-tasks/SKILL.md +0 -95
  90. package/shared/build-kit/lib/agent.sh +0 -20
  91. package/shared/build-kit/ralph.sh +0 -111
  92. package/shared/build-kit/realtime-agent.js +0 -20
  93. package/stacks/bridge/templates/bridge/lib/AGENT.md +0 -47
  94. package/stacks/bridge/templates/bridge/lib/prompt.md +0 -111
  95. package/stacks/bridge/templates/bridge/ralph-claude.js +0 -50
  96. package/stacks/bridge/templates/bridge/ralph-hook.js +0 -103
  97. package/stacks/bridge/templates/bridge/ralph-local-ai.js +0 -43
  98. package/stacks/bridge/templates/bridge/ralph-static.js +0 -90
@@ -1,125 +0,0 @@
1
- ---
2
- name: bridge-spec-kitty-specify
3
- description: "Adapter: generate a Spec Kitty mission spec.md (and auto-evaluate its quality checklist) from an Eventmodelers board's exported event model, instead of running the /spec-kitty.specify interview. Use when the user says \"use the event model as the spec\", \"sync the board to spec-kitty\", \"generate the spec from the event model\", or when a .slices/ export exists and a Spec Kitty mission needs to start or refresh from it."
4
- ---
5
-
6
- # bridge-spec-kitty-specify
7
-
8
- Replaces the human interview in `/spec-kitty.specify` with a direct read of an
9
- Eventmodelers board export. The event model is the source of truth; `spec.md`
10
- becomes a **generated, read-only artifact** derived from it. Everything
11
- downstream of specify (`/spec-kitty.plan`, `/spec-kitty.tasks`,
12
- implement/review/merge) is untouched — this skill only produces the two
13
- artifacts that phase gate expects: `spec.md` and `checklists/requirements.md`.
14
-
15
- Do not use this to hand-author or hand-edit `spec.md` — if the event model is
16
- wrong, fix it on the board and re-run this skill. A `spec.md` produced here
17
- that gets manually edited afterward will be silently overwritten on the next
18
- sync; there is no merge/diff in this version (see Limitations).
19
-
20
- ## Inputs
21
-
22
- The Eventmodelers `listen` server (`npx @eventmodelers/cli listen`) writes the
23
- board export locally. Read it directly — do not call the board API:
24
-
25
- - `.slices/current_context.json` → `{ "name": "<context>" }` — which context
26
- to ingest if the user didn't name one.
27
- - `.slices/<context>/index.json` → `{ "slices": [{ id, slice, index, context,
28
- folder, status, group }] }` — the ordered slice list.
29
- - `.slices/<context>/context.json` → context-level name/package info.
30
- - `.slices/<context>/<folder>/slice.json` → full slice detail per the
31
- [event-modeling-spec](https://github.com/dilgerma/event-modeling-spec)
32
- contract: `commands[]`, `events[]`, `readModels[]` (projections/queries),
33
- `screens[]`, `automations[]`/`processors[]`, `specifications[]` (given/when/
34
- then), plus free-text `description`/`notes`.
35
-
36
- If `.slices/` doesn't exist yet, stop and tell the user to run
37
- `npx @eventmodelers/cli listen` (or `init-modeling` first if the kit isn't
38
- installed) and push the board export before retrying.
39
-
40
- ## Flow
41
-
42
- 1. **Resolve the mission.** If an existing `kitty-specs/<slug>/meta.json`
43
- already points at this context (check `purpose_tldr`/a stored context
44
- reference), re-sync into it. Otherwise create a new mission dir the same
45
- way `/spec-kitty.specify` does: kebab-case name + 8-char ULID suffix,
46
- `mission_type: software-dev` (this adapter does not introduce a new
47
- mission type — see project memory on why). Write `meta.json` matching the
48
- shape in an existing mission (`mission_id`, `mission_slug`, `purpose_tldr`,
49
- `purpose_context`, `target_branch`, etc.).
50
- 2. **Load every slice** referenced in `index.json` by reading each
51
- `<folder>/slice.json`. Do not skip slices with empty `specifications` —
52
- surface them as gaps in the checklist instead (step 4).
53
- 3. **Compose `spec.md`** using the standard Spec Kitty template sections,
54
- sourced only from what the model actually states — never invent detail
55
- the board doesn't have:
56
- - **Purpose** ← `context.json` name/description, or ask the user for a
57
- one-paragraph purpose if the board has none.
58
- - **Primary User Story** ← walk the slices in `index.json` order (this is
59
- already the chronological/timeline order from the board) and narrate
60
- the SCREEN → COMMAND → EVENT → READMODEL flow across them.
61
- - **Acceptance Scenarios** ← one bullet per `specifications[]` entry,
62
- copied straight from its given/when/then — do not paraphrase away
63
- precision the model already captured.
64
- - **Edge Cases** ← `specifications[]` entries that describe rejection/
65
- negative/boundary behavior (reject, invalid, already-exists, etc.).
66
- - **Domain Language** ← distinct nouns from event/command/read-model field
67
- names and titles, deduped.
68
- - **Functional Requirements (FR-###)** ← one row per COMMAND (state-change
69
- slice) and per AUTOMATION slice; wording from the command/automation
70
- name + `description`.
71
- - **Non-Functional Requirements / Constraints** ← only from explicit
72
- `notes`/`description` content tagged as timing/authorization/volume
73
- constraints. If the model states none, leave the table empty — do not
74
- fabricate NFRs to fill it.
75
- - **Key Entities** ← distinct read-model/event payload shapes.
76
- - **Interfaces to Other Teams** ← any `TRANSLATION`-type slice or external
77
- system mentioned in a slice's `description`.
78
- 4. **Evaluate `checklists/requirements.md`** against the ingested model
79
- instead of a human review pass. Check an item only if the model
80
- demonstrably satisfies it:
81
- - "No [NEEDS CLARIFICATION] markers remain" → fails if any slice has an
82
- empty `specifications[]` or a `description` containing `TODO`/`TBD`.
83
- - "Requirements are testable and unambiguous" → fails if an FR/NFR row has
84
- no backing `specifications[]` entry.
85
- - "Non-functional requirements include measurable thresholds" → fails
86
- (leave unchecked, don't skip the row) if the NFR table is empty but the
87
- model contains AUTOMATION slices implying a timing constraint the board
88
- never made explicit.
89
- - Leave any check unchecked with a `## Notes` explanation when the event
90
- model genuinely doesn't provide enough to verify it — this checklist
91
- gates `/spec-kitty.plan`, so a false pass defeats the point of the gate.
92
- 5. **Hand off.** Once `spec.md` + `checklists/requirements.md` are written,
93
- tell the user the mission is ready for `/spec-kitty.plan` as normal — do
94
- not run plan/tasks yourself from this skill.
95
-
96
- ## Re-sync behavior
97
-
98
- Re-running this skill against the same mission regenerates `spec.md` and
99
- `checklists/requirements.md` wholesale from the current `.slices/` state.
100
- There is no field-level diff against the previous generation — if the spec
101
- needs to reflect board changes, re-run this skill rather than hand-patching
102
- `spec.md`.
103
-
104
- ## Guardrail
105
-
106
- If a required section can't be populated from the event model (e.g. no
107
- Purpose anywhere on the board), ask the user rather than inventing one — an
108
- invented Purpose defeats the reason this adapter exists (the event model,
109
- not the LLM, is the source of truth for product intent).
110
-
111
- ## Limitations (by design, v1)
112
-
113
- - Does not itself generate `tasks/WP##-*.md` from slices — see the companion
114
- `bridge-spec-kitty-tasks` skill, which enforces a 1:1
115
- slice-to-WP mapping when `wps.yaml` is written during
116
- `/spec-kitty.tasks-outline`.
117
- - Does not introduce a new Spec Kitty mission type. Spec Kitty 3.2.5's
118
- built-in mission types are loaded from a flat directory inside the
119
- installed package (`doctrine/missions/mission_types/*.yaml`) via
120
- `MissionTypeRepository.default()`, and org-pack extension for the
121
- `mission_types` kind is unwired in this version (fragments only add
122
- governance-graph metadata, not an executable type). A real native mission
123
- type would require patching the installed `spec-kitty-cli` package itself.
124
- This adapter avoids that entirely — it stays inside `mission_type:
125
- software-dev` and only replaces the specify phase's *inputs*.
@@ -1,95 +0,0 @@
1
- ---
2
- name: bridge-spec-kitty-tasks
3
- description: "Adapter: enforce a 1:1 mapping between Eventmodelers board slices and Spec Kitty work packages when writing wps.yaml. Use when the user says \"one slice should become one task/WP\", \"generate tasks from the event model\", or right after bridge-spec-kitty-specify, in place of freeform subtask-grouping judgment in /spec-kitty.tasks-outline."
4
- ---
5
-
6
- # bridge-spec-kitty-tasks
7
-
8
- Companion to `bridge-spec-kitty-specify`. That skill turns the event model into
9
- `spec.md`; this one enforces that each **slice** on the board becomes exactly
10
- **one work package** — never split across WPs, never merged with another
11
- slice — when Spec Kitty's task-planning pipeline builds `wps.yaml`.
12
-
13
- This replaces only step 3–6 of `/spec-kitty.tasks-outline` (deriving
14
- subtasks and rolling them into WPs). `/spec-kitty.tasks-packages` and
15
- `/spec-kitty.tasks-finalize` run completely unmodified afterward — they only
16
- ever consume `wps.yaml`, and don't care where its groupings came from.
17
-
18
- ## Why a hard 1:1 boundary
19
-
20
- Spec Kitty's default `tasks-outline` groups subtasks into WPs by
21
- judgment (target 3–7 subtasks, merge small ones, split large ones). That's
22
- the right default when subtask boundaries come from an LLM reading prose. It
23
- is the *wrong* default here: the event model already drew the real
24
- boundaries (each SLICE_BORDER is a reviewable, independently buildable unit
25
- the board's `Planned → InProgress → Done` slice lifecycle already governs).
26
- Re-grouping at the WP layer would create a second, competing notion of "unit
27
- of work" that drifts from the board over time. So this skill trades away
28
- `tasks-outline`'s sizing flexibility on purpose: WP boundary = slice
29
- boundary, full stop.
30
-
31
- ## Inputs
32
-
33
- Same event-model export as `bridge-spec-kitty-specify`:
34
- `.slices/<context>/index.json` (slice list, board/timeline order) and each
35
- `.slices/<context>/<folder>/slice.json` (commands, events, readModels,
36
- automations, `specifications`). Also read, from `feature_dir`:
37
- `spec.md` (for FR/NFR ids — already generated by `bridge-spec-kitty-specify`) and
38
- `plan.md` (for the tech-stack/file-layout context needed to fill
39
- `owned_files`, since the event model doesn't know the codebase's directory
40
- structure).
41
-
42
- ## Flow
43
-
44
- 1. **One WP per slice, in board order.** Assign `WP01`, `WP02`, ... following
45
- `index.json`'s order (already the board's chronological/timeline order).
46
- Do not reorder for "phase grouping" the way default `tasks-outline` does —
47
- the board order already encodes the intended build sequence.
48
- 2. **Subtasks come from inside the slice, not across slices.** For each
49
- slice, break its own commands/read-models/screens/automations into T-ids
50
- (one per command handler, one per read-model endpoint, one per
51
- screen/automation, one for its tests) — all nested under that slice's one
52
- WP. Every subtask must trace to exactly one slice; never let a subtask
53
- span two slices.
54
- 3. **Dependencies from event flow, not guesswork.** WP *B* depends on WP *A*
55
- iff slice B's commands/queries/automations consume an event that slice
56
- A's commands/automations produce. Derive this by diffing each slice's
57
- emitted event names against every other slice's consumed event names —
58
- this is mechanical, not judgment-based, so don't also apply
59
- `tasks-outline`'s "Phase 2 typically depends on Phase 1" heuristic on top
60
- of it.
61
- 4. **`requirement_refs` from the spec's own FR/NFR table.** Match each
62
- slice's command/read-model/automation names against `spec.md`'s FR/NFR
63
- rows (written by `bridge-spec-kitty-specify` from these same slices) and cite
64
- whichever ids trace to this slice. A slice with no matching row means
65
- `bridge-spec-kitty-specify` needs a re-run, not a fabricated ref here.
66
- 5. **Write `wps.yaml`** using the exact schema `/spec-kitty.tasks-outline`
67
- expects (`id`, `title`, `dependencies`, `owned_files`, `requirement_refs`,
68
- `plan_concern_refs` or `cross_cutting: true`, `subtasks`, `prompt_file:
69
- null`) — so `tasks-packages`/`finalize-tasks` need no awareness that this
70
- skill (rather than default `tasks-outline`) produced it.
71
- 6. **Hand off.** Tell the user to continue with `/spec-kitty.tasks-packages`
72
- then `/spec-kitty.tasks-finalize` (or `spec-kitty next`) exactly as normal.
73
-
74
- ## Guardrail: don't fix boundary problems here
75
-
76
- If a slice's own scope would blow past Spec Kitty's WP sizing ceiling (>10
77
- subtasks / >700 lines), **do not silently split it into two WPs** — that
78
- breaks the 1:1 invariant this skill exists to enforce, and produces the
79
- exact spec/code drift it's meant to prevent. Instead, flag it to the user:
80
- the slice is too coarse on the board itself, so split the SLICE_BORDER
81
- there and re-run both `bridge-spec-kitty-specify` and this skill. The event model
82
- stays the single place slice boundaries get decided.
83
-
84
- The same holds in the other direction: never merge two small slices into
85
- one WP for tidiness, even if `tasks-outline`'s default guidance would.
86
-
87
- ## Limitations (v1)
88
-
89
- - `owned_files`/file-layout ownership still comes from `plan.md`, which is
90
- still produced by the normal, LLM-authored `/spec-kitty.plan` — the event
91
- model has no notion of source-file structure, so this isn't something to
92
- push onto it.
93
- - Re-running this skill after the board changes regenerates `wps.yaml`
94
- wholesale from the current slice set, same as `bridge-spec-kitty-specify`'s
95
- re-sync behavior — no field-level diff against a prior run.
@@ -1,20 +0,0 @@
1
- #!/bin/bash
2
- # Runs the AI agent with the given prompt in the project directory.
3
- # Called by ralph.sh with cwd already set to the project root.
4
- # Usage: ./agent.sh "<prompt>"
5
-
6
- set -euo pipefail
7
-
8
- PROMPT="${1:-}"
9
- if [[ -z "$PROMPT" ]]; then
10
- echo "ERROR: No prompt provided"
11
- exit 1
12
- fi
13
-
14
- claude --dangerously-skip-permissions -p "$PROMPT"
15
-
16
- # --- To use a local Ollama model instead, comment out the line above
17
- # and uncomment the block below. Run `ollama serve` first.
18
- #
19
- # MODEL="${OLLAMA_MODEL:-qwen3.5:9b}"
20
- # node "$(dirname "$0")/ollama-agent.js" "$MODEL"
@@ -1,111 +0,0 @@
1
- #!/bin/bash
2
- # Ralph agent loop — two independent loops, each triggered by their own condition
3
- #
4
- # onTask: tasks.json has entries → load slice from board, update .build-kit/.slices/
5
- # onPlannedSlice: .build-kit/.slices/ has a "Planned" slice → build it
6
- #
7
- # The loops are NOT causally linked — either can trigger on its own.
8
- #
9
- # Usage: ./ralph.sh [iterations] [project_dir]
10
- # iterations — number of loop cycles to run; 0 or omitted means run forever
11
- # project_dir — path to the project root; defaults to the parent of .build-kit
12
-
13
- set -euo pipefail
14
-
15
- KIT_DIR="$(cd "$(dirname "$0")" && pwd)"
16
- ITERATIONS="${1:-0}"
17
- PROJECT_DIR="${2:-"$KIT_DIR/.."}"
18
- TASKS_FILE="$KIT_DIR/tasks.json"
19
- PROMPT_FILE="$KIT_DIR/lib/prompt.md"
20
- BACKEND_PROMPT_FILE="$KIT_DIR/lib/backend-prompt.md"
21
- AGENT_SCRIPT="$KIT_DIR/lib/agent.sh"
22
-
23
- HAS_CREDENTIALS=true
24
- if [[ "${RALPH_LOCAL:-}" == "1" ]]; then
25
- echo "[ralph] --local — platform sync disabled." >&2
26
- HAS_CREDENTIALS=false
27
- elif [[ ! -f "$KIT_DIR/.eventmodelers/config.json" ]]; then
28
- echo "[ralph] Note: no .eventmodelers/config.json found — platform sync disabled." >&2
29
- echo " To enable board sync, follow: https://app.eventmodelers.ai/documentation#build" >&2
30
- echo " Code generation from local slice definitions will still run." >&2
31
- HAS_CREDENTIALS=false
32
- fi
33
-
34
- echo "Ralph — kit: $KIT_DIR project: $PROJECT_DIR"
35
-
36
- # Returns 0 if tasks.json has at least one task
37
- has_pending_tasks() {
38
- [[ -f "$TASKS_FILE" ]] || return 1
39
- local content
40
- content=$(cat "$TASKS_FILE")
41
- [[ "$content" != "[]" && -n "$content" ]]
42
- }
43
-
44
- # Status this loop picks work up from — `run --status-trigger <status>` (RALPH_STATUS_TRIGGER), default Planned
45
- PICKUP_STATUS="${RALPH_STATUS_TRIGGER:-Planned}"
46
-
47
- # Returns 0 if any JSON under .build-kit/.slices/ contains a slice in the pickup status
48
- has_planned_slices() {
49
- grep -rqi "\"status\"[[:space:]]*:[[:space:]]*\"$PICKUP_STATUS\"" "$KIT_DIR/.slices/" --include='index.json' 2>/dev/null
50
- }
51
-
52
- # Returns the title of the first "Planned" slice, or empty string
53
- get_planned_slice_title() {
54
- for index_file in "$KIT_DIR/.slices/"*/index.json; do
55
- [[ -f "$index_file" ]] || continue
56
- local title
57
- title=$(node -e "
58
- try {
59
- const d = JSON.parse(require('fs').readFileSync(process.argv[1], 'utf-8'));
60
- const s = (d.slices||[]).find(s => (s.status||'').toLowerCase() === process.argv[2].toLowerCase());
61
- if (s) process.stdout.write(s.slice || s.id || '');
62
- } catch(e) {}
63
- " "$index_file" "$PICKUP_STATUS" 2>/dev/null)
64
- if [[ -n "$title" ]]; then
65
- echo "$title"
66
- return
67
- fi
68
- done
69
- }
70
-
71
- # prompt.md and backend-prompt.md are instruction manuals with no request in them — handed one
72
- # alone, the agent may read it as background and ask what to do. Every turn ends with the concrete
73
- # request, the same one lib/ralph.js's taskRequest() appends.
74
- task_request() {
75
- printf '\n\n---\n\nYour task now: %s, following the instructions above. Start immediately.\n' "$1"
76
- }
77
-
78
- # Runs agent.sh with the given prompt; retries on non-zero exit
79
- run_agent() {
80
- local label="$1"
81
- local prompt="$2"
82
- while true; do
83
- echo "[$(date -u +%H:%M:%S)] $label"
84
- (cd "$PROJECT_DIR" && bash "$AGENT_SCRIPT" "$prompt") 2>&1 && return 0
85
- echo "[$(date -u +%H:%M:%S)] Agent error — retrying in 60s..."
86
- sleep 60
87
- done
88
- }
89
-
90
- cycle=0
91
- while [[ "$ITERATIONS" -eq 0 || "$cycle" -lt "$ITERATIONS" ]]; do
92
- ran_something=false
93
-
94
- if [[ "$HAS_CREDENTIALS" == true ]] && has_pending_tasks; then
95
- run_agent "onTask: loading slice from board..." "$(cat "$PROMPT_FILE"; task_request 'process the pending tasks in tasks.json')"
96
- ran_something=true
97
- fi
98
-
99
- if has_planned_slices; then
100
- slice_title=$(get_planned_slice_title)
101
- run_agent "onPlannedSlice: building \"$slice_title\"..." "$(cat "$BACKEND_PROMPT_FILE"; task_request "build the $PICKUP_STATUS slice \"$slice_title\"")"
102
- echo "[$(date -u +%H:%M:%S)] Slice \"$slice_title\" build complete — waiting for next slice"
103
- ran_something=true
104
- fi
105
-
106
- if [[ "$ran_something" == false ]]; then
107
- sleep 3
108
- fi
109
-
110
- (( cycle++ )) || true
111
- done
@@ -1,20 +0,0 @@
1
- #!/usr/bin/env node
2
- // Standalone realtime agent — subscribes to board events and writes tasks.json.
3
- // The same logic runs embedded inside ralph-claude.js / ralph-local-ai.js, so you
4
- // only need this if you want to run the agent independently (e.g. separate terminal).
5
- // Usage: node realtime-agent.js [kit_dir]
6
-
7
- import { loadLocalConfig, fetchPlatformConfig, retryTransient, startRealtimeAgent } from './lib/ralph.js';
8
- import { redactConsole } from './lib/redact.js';
9
- import { dirname, resolve } from 'path';
10
- import { fileURLToPath } from 'url';
11
-
12
- const kitDir = process.argv[2] ? resolve(process.argv[2]) : dirname(fileURLToPath(import.meta.url));
13
-
14
- const local = loadLocalConfig(kitDir);
15
- redactConsole([local.token]);
16
- const cfg = await retryTransient('fetchPlatformConfig', () => fetchPlatformConfig(local));
17
-
18
- console.log(`[agent] Starting — org=${cfg.organizationId}${cfg.boardId ? `, board=${cfg.boardId}` : ''}, base=${cfg.baseUrl}`);
19
-
20
- await startRealtimeAgent(cfg, kitDir);
@@ -1,47 +0,0 @@
1
- # Agent Learnings
2
-
3
- Patterns and gotchas discovered during task processing. Update this file
4
- whenever you encounter something reusable.
5
-
6
- ## tasks.json
7
-
8
- - Tasks are objects with `id`, `createdAt`, and `payload` (a
9
- `SliceChangedPayload`).
10
- - Unlike build-kit, every status change is queued (not just non-`Planned`
11
- ones) — see `queueAllStatuses` in `lib/ralph.js`.
12
- - After completing a task, remove it from the array entirely — do not add a
13
- status field.
14
- - Write `[]` to `tasks.json` if the last task is completed.
15
-
16
- ## SliceChangedPayload fields
17
-
18
- ```
19
- event always "slice:changed"
20
- organizationId org UUID or null
21
- boardId board UUID
22
- sliceId SLICE_BORDER node UUID
23
- sliceTitle human-readable slice name (may be null)
24
- sliceStatus e.g. "Created", "Planned", "InProgress", "Done" (may be null)
25
- timestamp unix ms when the change was emitted
26
- ```
27
-
28
- ## Bridge target
29
-
30
- - The active target framework lives in `.bridge-kit/bridge.json`'s `target`
31
- field, written at `init --bridge --target <name>` time. This file is
32
- committed (unlike `.eventmodelers/config.json`, which is gitignored) since
33
- it's project policy shared by every teammate and CI runner, not a
34
- per-machine credential.
35
- - Skill naming convention: `bridge-<target>-specify` (kept in sync every
36
- task) and `bridge-<target>-tasks` (invoked separately, by that framework's
37
- own task-planning phase — not from this loop).
38
-
39
- ## Executors
40
-
41
- - Claude (`ralph-claude.js`, this prompt) is the default. `bridge --local-ai`
42
- swaps in a local model instead — same prompt, different executor.
43
- - `bridge --hook "<command>"` (or a `hookCommand` persisted in
44
- `bridge.json`) bypasses this prompt entirely: `ralph-hook.js` runs an
45
- arbitrary shell command per batch of changes instead of any AI agent — e.g.
46
- commit + push `.slices/` and let a CI pipeline do the actual translation.
47
- If you're reading this file, a hook wasn't configured for this run.
@@ -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
- });