create-merlin-brain 3.7.2 → 3.8.0-beta.0

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.
@@ -0,0 +1,367 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # ╔═══════════════════════════════════════════════════════════════════════════╗
4
+ # ║ MERLIN WORKFLOW ENGINE - Core Engine ║
5
+ # ║ Load, validate, execute steps, manage state ║
6
+ # ╚═══════════════════════════════════════════════════════════════════════════╝
7
+ #
8
+ # A thin orchestration layer on top of existing primitives:
9
+ # - Agent selection → blend_for_task() (blend.sh)
10
+ # - Context passing → handoff_append() (blend-handoff.sh)
11
+ # - Verification → blend_and_verify() (blend-verify.sh)
12
+ # - Parallel steps → execute_wave_teams() (teams.sh)
13
+ # - Learning → learn_record() (blend-learn.sh)
14
+ #
15
+ # This file: core engine (load, template, step execution, state)
16
+ # See workflow-run.sh: orchestration (run, resume, status, CLI dispatch)
17
+ #
18
+ # Requires: blend.sh, blend-handoff.sh loaded first
19
+
20
+ # Colors
21
+ : "${RESET:=\033[0m}"
22
+ : "${BOLD:=\033[1m}"
23
+ : "${DIM:=\033[2m}"
24
+ : "${RED:=\033[31m}"
25
+ : "${GREEN:=\033[32m}"
26
+ : "${YELLOW:=\033[33m}"
27
+ : "${BLUE:=\033[34m}"
28
+ : "${MAGENTA:=\033[35m}"
29
+ : "${CYAN:=\033[36m}"
30
+
31
+ # ═══════════════════════════════════════════════════════════════════════════════
32
+ # Configuration
33
+ # ═══════════════════════════════════════════════════════════════════════════════
34
+
35
+ WORKFLOW_DIR="${HOME}/.claude/loop/workflows"
36
+ WORKFLOW_BUNDLED_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/workflows"
37
+ WORKFLOW_RUN_FILE="${MERLIN_LOOP_DIR:-.merlin-loop}/workflow-run.json"
38
+
39
+ # ═══════════════════════════════════════════════════════════════════════════════
40
+ # Load & Validate
41
+ # ═══════════════════════════════════════════════════════════════════════════════
42
+
43
+ # Load a workflow definition by ID
44
+ # Returns the JSON content on stdout, errors to stderr
45
+ workflow_load() {
46
+ local id="$1"
47
+ local wf_file=""
48
+
49
+ # Search order: user dir → bundled dir
50
+ if [ -f "${WORKFLOW_DIR}/${id}.json" ]; then
51
+ wf_file="${WORKFLOW_DIR}/${id}.json"
52
+ elif [ -f "${WORKFLOW_BUNDLED_DIR}/${id}.json" ]; then
53
+ wf_file="${WORKFLOW_BUNDLED_DIR}/${id}.json"
54
+ else
55
+ echo -e "${RED}Workflow '${id}' not found.${RESET}" >&2
56
+ echo -e "${DIM}Searched: ${WORKFLOW_DIR}/ and ${WORKFLOW_BUNDLED_DIR}/${RESET}" >&2
57
+ return 1
58
+ fi
59
+
60
+ # Validate: valid JSON with required fields
61
+ if ! python3 -c "
62
+ import json, sys
63
+ with open('$wf_file') as f:
64
+ wf = json.load(f)
65
+ assert 'id' in wf, 'Missing id'
66
+ assert 'steps' in wf, 'Missing steps'
67
+ assert len(wf['steps']) > 0, 'No steps defined'
68
+ print(json.dumps(wf))
69
+ " 2>/dev/null; then
70
+ echo -e "${RED}Invalid workflow definition: ${wf_file}${RESET}" >&2
71
+ return 1
72
+ fi
73
+ }
74
+
75
+ # List all available workflows
76
+ workflow_list() {
77
+ echo -e "${MAGENTA}${BOLD} Available Workflows${RESET}"
78
+ echo -e "${MAGENTA} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
79
+
80
+ local found=0
81
+ for dir in "$WORKFLOW_BUNDLED_DIR" "$WORKFLOW_DIR"; do
82
+ [ ! -d "$dir" ] && continue
83
+ for wf_file in "$dir"/*.json; do
84
+ [ ! -f "$wf_file" ] && continue
85
+ found=1
86
+
87
+ local id name desc step_count
88
+ id=$(python3 -c "import json; wf=json.load(open('$wf_file')); print(wf.get('id','?'))" 2>/dev/null)
89
+ name=$(python3 -c "import json; wf=json.load(open('$wf_file')); print(wf.get('name','?'))" 2>/dev/null)
90
+ desc=$(python3 -c "import json; wf=json.load(open('$wf_file')); print(wf.get('description',''))" 2>/dev/null)
91
+ step_count=$(python3 -c "import json; wf=json.load(open('$wf_file')); print(len(wf.get('steps',[])))" 2>/dev/null)
92
+
93
+ local source_tag=""
94
+ [[ "$dir" == "$WORKFLOW_DIR" ]] && source_tag="${CYAN}[user]${RESET}" || source_tag="${DIM}[bundled]${RESET}"
95
+
96
+ echo -e " ${GREEN}${BOLD}${id}${RESET} ${source_tag} ${step_count} steps"
97
+ echo -e " ${name} — ${DIM}${desc}${RESET}"
98
+ echo ""
99
+ done
100
+ done
101
+
102
+ [ "$found" -eq 0 ] && echo -e " ${DIM}No workflows found. Place .json files in: ${WORKFLOW_DIR}/${RESET}"
103
+ }
104
+
105
+ # ═══════════════════════════════════════════════════════════════════════════════
106
+ # Run Initialization
107
+ # ═══════════════════════════════════════════════════════════════════════════════
108
+
109
+ workflow_run_init() {
110
+ local workflow_json="$1"
111
+ local task="$2"
112
+
113
+ local wf_id run_id
114
+ wf_id=$(echo "$workflow_json" | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])")
115
+ run_id="${wf_id}-$(date +%s | tail -c 7)"
116
+
117
+ mkdir -p "$(dirname "$WORKFLOW_RUN_FILE")"
118
+
119
+ python3 << PYEOF
120
+ import json, sys
121
+ from datetime import datetime, timezone
122
+
123
+ wf = json.loads('''$workflow_json''')
124
+ task = """$task"""
125
+
126
+ run = {
127
+ "run_id": "$run_id",
128
+ "workflow": wf["id"],
129
+ "workflow_name": wf.get("name", wf["id"]),
130
+ "task": task,
131
+ "started": datetime.now(timezone.utc).isoformat(),
132
+ "status": "running",
133
+ "current_step": wf["steps"][0]["id"],
134
+ "steps": {}
135
+ }
136
+
137
+ for step in wf["steps"]:
138
+ run["steps"][step["id"]] = {"status": "pending"}
139
+
140
+ with open("$WORKFLOW_RUN_FILE", 'w') as f:
141
+ json.dump(run, f, indent=2)
142
+
143
+ print("$run_id")
144
+ PYEOF
145
+ }
146
+
147
+ # ═══════════════════════════════════════════════════════════════════════════════
148
+ # Template Substitution
149
+ # ═══════════════════════════════════════════════════════════════════════════════
150
+
151
+ workflow_apply_template() {
152
+ local template="$1"
153
+ local task="$2"
154
+ local handoff_context="$3"
155
+ local session_dir="$4"
156
+
157
+ python3 << PYEOF
158
+ import re, os
159
+
160
+ template = """$template"""
161
+ task = """$task"""
162
+ handoff = """$handoff_context"""
163
+ session_dir = "$session_dir"
164
+
165
+ result = template.replace("{{task}}", task).replace("{{handoff}}", handoff)
166
+
167
+ def replace_file_ref(match):
168
+ filename = match.group(1)
169
+ filepath = os.path.join(session_dir, filename)
170
+ if os.path.isfile(filepath):
171
+ with open(filepath, 'r') as f:
172
+ return f.read()
173
+ return f"[File not found: {filename}]"
174
+
175
+ result = re.sub(r'\{\{([a-zA-Z0-9_.-]+)\}\}', replace_file_ref, result)
176
+ print(result)
177
+ PYEOF
178
+ }
179
+
180
+ # ═══════════════════════════════════════════════════════════════════════════════
181
+ # Step Execution
182
+ # ═══════════════════════════════════════════════════════════════════════════════
183
+
184
+ # Execute a single workflow step with retry, blend engine, and handoff support
185
+ workflow_step_run() {
186
+ local step_json="$1"
187
+ local task="$2"
188
+ local handoff_context="$3"
189
+ local session_dir="$4"
190
+
191
+ # Parse step fields via single python call for efficiency
192
+ local step_fields
193
+ step_fields=$(echo "$step_json" | python3 -c "
194
+ import json, sys
195
+ s = json.load(sys.stdin)
196
+ print(s['id'])
197
+ print(s.get('label', s['id']))
198
+ print(s.get('agent_hint', 'impl'))
199
+ print(s.get('agent_override', ''))
200
+ print(s.get('input_template', '{{task}}\n\n{{handoff}}'))
201
+ print('---FIELD_SEP---')
202
+ print(s.get('expects', ''))
203
+ print(s.get('retry', 1))
204
+ print(s.get('blend', False))
205
+ print(s.get('independent', False))
206
+ print(s.get('parallel', False))
207
+ print(s.get('output_file', ''))
208
+ print(s.get('action', ''))
209
+ ")
210
+
211
+ local step_id step_label agent_hint agent_override input_template
212
+ local expects_signal retry_max use_blend is_independent is_parallel output_file action
213
+
214
+ step_id=$(echo "$step_fields" | sed -n '1p')
215
+ step_label=$(echo "$step_fields" | sed -n '2p')
216
+ agent_hint=$(echo "$step_fields" | sed -n '3p')
217
+ agent_override=$(echo "$step_fields" | sed -n '4p')
218
+ # input_template spans lines 5 to the separator
219
+ input_template=$(echo "$step_fields" | sed -n '5,/---FIELD_SEP---/p' | sed '$d')
220
+ expects_signal=$(echo "$step_fields" | sed -n '/---FIELD_SEP---/,$ p' | sed -n '2p')
221
+ retry_max=$(echo "$step_fields" | sed -n '/---FIELD_SEP---/,$ p' | sed -n '3p')
222
+ use_blend=$(echo "$step_fields" | sed -n '/---FIELD_SEP---/,$ p' | sed -n '4p')
223
+ is_independent=$(echo "$step_fields" | sed -n '/---FIELD_SEP---/,$ p' | sed -n '5p')
224
+ is_parallel=$(echo "$step_fields" | sed -n '/---FIELD_SEP---/,$ p' | sed -n '6p')
225
+ output_file=$(echo "$step_fields" | sed -n '/---FIELD_SEP---/,$ p' | sed -n '7p')
226
+ action=$(echo "$step_fields" | sed -n '/---FIELD_SEP---/,$ p' | sed -n '8p')
227
+
228
+ local prompt
229
+ prompt=$(workflow_apply_template "$input_template" "$task" "$handoff_context" "$session_dir")
230
+
231
+ _workflow_update_step "$step_id" "running"
232
+
233
+ local attempt=0 output="" exit_code=1
234
+
235
+ while [ "$attempt" -lt "$retry_max" ]; do
236
+ attempt=$((attempt + 1))
237
+ echo -e " ${CYAN}[attempt ${attempt}/${retry_max}]${RESET}" >&2
238
+
239
+ # Choose execution method based on step flags
240
+ if [ "$is_independent" = "True" ] && type blend_and_verify &>/dev/null; then
241
+ output=$(blend_and_verify "$prompt" "" "$session_dir/step-${step_id}")
242
+ exit_code=$?
243
+ elif [ "$is_parallel" = "True" ] && type execute_wave_teams &>/dev/null && teams_enabled 2>/dev/null; then
244
+ output=$(blend_and_spawn "$prompt" "" "$session_dir/step-${step_id}")
245
+ exit_code=$?
246
+ elif [ -n "$agent_override" ] && [ "$use_blend" != "True" ]; then
247
+ local agent_path="${HOME}/.claude/agents/${agent_override}.md"
248
+ if [ -f "$agent_path" ]; then
249
+ output=$(echo "$prompt" | claude --agent "$agent_override" -p --permission-mode acceptEdits --output-format text 2>&1)
250
+ exit_code=$?
251
+ else
252
+ output=$(blend_and_spawn "$prompt" "" "$session_dir/step-${step_id}" 2>/dev/null || echo "$prompt" | claude -p --output-format text 2>&1)
253
+ exit_code=$?
254
+ fi
255
+ elif type blend_and_spawn &>/dev/null; then
256
+ output=$(blend_and_spawn "$prompt" "" "$session_dir/step-${step_id}")
257
+ exit_code=$?
258
+ else
259
+ output=$(echo "$prompt" | claude -p --output-format text 2>&1)
260
+ exit_code=$?
261
+ fi
262
+
263
+ # Check expects signal
264
+ if [ $exit_code -eq 0 ]; then
265
+ if [ -z "$expects_signal" ] || echo "$output" | grep -qi "$expects_signal"; then
266
+ break
267
+ else
268
+ echo -e " ${YELLOW}Expected signal '${expects_signal}' not found, retrying...${RESET}" >&2
269
+ exit_code=1
270
+ fi
271
+ fi
272
+ done
273
+
274
+ # Save output file if specified
275
+ [ -n "$output_file" ] && [ $exit_code -eq 0 ] && echo "$output" > "$session_dir/$output_file"
276
+
277
+ # Update handoff chain
278
+ if type handoff_append &>/dev/null && [ -f "$session_dir/handoff.json" ]; then
279
+ handoff_append "$session_dir/handoff.json" "$step_id" "$output"
280
+ fi
281
+
282
+ # Run post-step action
283
+ [ $exit_code -eq 0 ] && [ -n "$action" ] && _workflow_run_action "$action" "$output" "$session_dir"
284
+
285
+ # Update run state
286
+ [ $exit_code -eq 0 ] && _workflow_update_step "$step_id" "done" "$attempt" || _workflow_update_step "$step_id" "failed" "$attempt"
287
+
288
+ echo "$output"
289
+ return $exit_code
290
+ }
291
+
292
+ # ═══════════════════════════════════════════════════════════════════════════════
293
+ # State Management
294
+ # ═══════════════════════════════════════════════════════════════════════════════
295
+
296
+ _workflow_update_step() {
297
+ local step_id="$1"
298
+ local status="$2"
299
+ local attempts="${3:-1}"
300
+
301
+ [ ! -f "$WORKFLOW_RUN_FILE" ] && return
302
+
303
+ python3 << PYEOF
304
+ import json
305
+ from datetime import datetime, timezone
306
+
307
+ with open("$WORKFLOW_RUN_FILE", 'r') as f:
308
+ run = json.load(f)
309
+
310
+ step = run["steps"].get("$step_id", {})
311
+ step["status"] = "$status"
312
+ step["attempts"] = $attempts
313
+
314
+ if "$status" == "running":
315
+ step["started"] = datetime.now(timezone.utc).isoformat()
316
+ elif "$status" in ("done", "failed", "skipped"):
317
+ step["completed"] = datetime.now(timezone.utc).isoformat()
318
+
319
+ run["steps"]["$step_id"] = step
320
+
321
+ if "$status" == "done":
322
+ wf_steps = list(run["steps"].keys())
323
+ idx = wf_steps.index("$step_id") if "$step_id" in wf_steps else -1
324
+ if idx >= 0 and idx + 1 < len(wf_steps):
325
+ run["current_step"] = wf_steps[idx + 1]
326
+ else:
327
+ run["current_step"] = None
328
+ run["status"] = "completed"
329
+ elif "$status" == "failed":
330
+ run["status"] = "failed"
331
+
332
+ with open("$WORKFLOW_RUN_FILE", 'w') as f:
333
+ json.dump(run, f, indent=2)
334
+ PYEOF
335
+ }
336
+
337
+ # ═══════════════════════════════════════════════════════════════════════════════
338
+ # Post-Step Actions
339
+ # ═══════════════════════════════════════════════════════════════════════════════
340
+
341
+ _workflow_run_action() {
342
+ local action="$1"
343
+ local output="$2"
344
+ local session_dir="$3"
345
+
346
+ case "$action" in
347
+ gh_pr_create)
348
+ echo -e "${CYAN} Action: Create PR${RESET}" >&2
349
+ echo "$output" | grep -qi "pull request\|PR created\|pr/" || \
350
+ echo -e "${YELLOW} PR may not have been created. Check manually.${RESET}" >&2
351
+ ;;
352
+ git_commit)
353
+ echo -e "${CYAN} Action: Git commit${RESET}" >&2
354
+ git add -A && git commit -m "workflow: auto-commit from step" 2>/dev/null || true
355
+ ;;
356
+ notify)
357
+ type send_notification &>/dev/null && send_notification "Workflow Step Complete" "Step finished"
358
+ ;;
359
+ esac
360
+ }
361
+
362
+ # ═══════════════════════════════════════════════════════════════════════════════
363
+ # Load orchestration module
364
+ # ═══════════════════════════════════════════════════════════════════════════════
365
+
366
+ _WORKFLOW_LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
367
+ source "${_WORKFLOW_LIB_DIR}/workflow-run.sh" 2>/dev/null || true
@@ -47,6 +47,7 @@ source "$SCRIPT_DIR/lib/teams.sh" 2>/dev/null || true # Agent Teams integra
47
47
  # Stage 2: blend-parallel.sh — Parallel Agent Execution
48
48
  # Stage 3: blend-handoff.sh — Structured Handoffs
49
49
  # Stage 4: blend-learn.sh — Blend Learning
50
+ source "$SCRIPT_DIR/lib/workflow.sh" 2>/dev/null || true # Workflow engine (multi-agent pipelines)
50
51
  source "$SCRIPT_DIR/lib/boot.sh" 2>/dev/null || true # Boot sequence
51
52
  source "$SCRIPT_DIR/lib/session-end.sh" 2>/dev/null || true # Session end protocol
52
53
  source "$SCRIPT_DIR/lib/tui.sh" 2>/dev/null || true # Interactive TUI
@@ -769,6 +770,13 @@ usage() {
769
770
  echo " status Show current loop status"
770
771
  echo " reset Reset loop state (keeps history)"
771
772
  echo ""
773
+ echo " workflow list List available workflows"
774
+ echo " workflow run <id> \"<task>\" Run a named workflow"
775
+ echo " workflow status Show workflow run progress"
776
+ echo " workflow resume Resume interrupted workflow"
777
+ echo " workflow skip Skip current step and advance"
778
+ echo " workflow install <url|file> Install community workflow"
779
+ echo ""
772
780
  echo "Options:"
773
781
  echo " --mode MODE Loop mode: auto, interactive, hybrid (default: hybrid)"
774
782
  echo " auto - Only pause on explicit checkpoints"
@@ -792,6 +800,11 @@ usage() {
792
800
  echo " merlin-loop --max 100 build # Allow up to 100 iterations"
793
801
  echo " merlin-loop --teams build # Parallel wave execution (experimental)"
794
802
  echo " merlin-loop --afk auto # Run unattended (auto mode)"
803
+ echo ""
804
+ echo " merlin-loop workflow list # See available workflows"
805
+ echo " merlin-loop workflow run feature-dev \"Add OAuth\" # Run a workflow"
806
+ echo " merlin-loop workflow status # Check progress"
807
+ echo " merlin-loop workflow resume # Resume after failure"
795
808
  }
796
809
 
797
810
  parse_args() {
@@ -854,6 +867,13 @@ parse_args() {
854
867
  COMMAND="$1"
855
868
  shift
856
869
  ;;
870
+ workflow)
871
+ COMMAND="workflow"
872
+ shift
873
+ # Capture remaining args for workflow subcommand
874
+ WORKFLOW_ARGS=("$@")
875
+ break
876
+ ;;
857
877
  -h|--help)
858
878
  usage
859
879
  exit 0
@@ -869,6 +889,7 @@ parse_args() {
869
889
 
870
890
  main() {
871
891
  COMMAND="${COMMAND:-auto}"
892
+ WORKFLOW_ARGS=()
872
893
 
873
894
  parse_args "$@"
874
895
 
@@ -904,6 +925,15 @@ main() {
904
925
  reset_state
905
926
  echo -e "${GREEN}Loop state reset${RESET}"
906
927
  ;;
928
+ workflow)
929
+ merlin_banner
930
+ if type workflow_dispatch &>/dev/null; then
931
+ workflow_dispatch "${WORKFLOW_ARGS[@]}"
932
+ else
933
+ echo -e "${RED}Workflow engine not available. Check lib/workflow.sh${RESET}"
934
+ exit 1
935
+ fi
936
+ ;;
907
937
  *)
908
938
  usage
909
939
  exit 1
@@ -0,0 +1,64 @@
1
+ {
2
+ "id": "bug-fix",
3
+ "name": "Bug Fix",
4
+ "description": "Report a bug. Get back a fix with regression test and PR.",
5
+ "version": "1.0",
6
+ "steps": [
7
+ {
8
+ "id": "triage",
9
+ "label": "Triage & Reproduce",
10
+ "agent_hint": "debug",
11
+ "input_template": "Triage this bug report and reproduce it:\n\n{{task}}\n\nSteps:\n1. Understand the reported behavior\n2. Find the relevant code\n3. Reproduce the issue (or confirm from code analysis)\n4. Identify root cause vs symptoms\n5. Assess severity and blast radius\n\nOutput TRIAGE_COMPLETE with root cause analysis.",
12
+ "expects": "TRIAGE_COMPLETE",
13
+ "output_file": "triage.md",
14
+ "retry": 2
15
+ },
16
+ {
17
+ "id": "investigate",
18
+ "label": "Deep Investigation",
19
+ "agent_hint": "debug",
20
+ "input_template": "Deep-dive into the root cause identified in triage:\n\n{{triage.md}}\n\nPrevious context:\n{{handoff}}\n\nTrace the exact code path. Identify all affected branches. Find related bugs that might share the same root cause.\n\nOutput INVESTIGATION_COMPLETE with fix strategy.",
21
+ "expects": "INVESTIGATION_COMPLETE",
22
+ "output_file": "investigation.md",
23
+ "retry": 1
24
+ },
25
+ {
26
+ "id": "fix",
27
+ "label": "Apply Fix",
28
+ "agent_hint": "impl",
29
+ "input_template": "Apply the fix based on investigation:\n\n{{investigation.md}}\n\nPrevious context:\n{{handoff}}\n\nFix the root cause, not just the symptom. Keep changes minimal and focused. Commit atomically.\n\nOutput FIX_COMPLETE when the fix is applied.",
30
+ "expects": "FIX_COMPLETE",
31
+ "retry": 2,
32
+ "blend": true
33
+ },
34
+ {
35
+ "id": "regression-test",
36
+ "label": "Regression Test",
37
+ "agent_hint": "test",
38
+ "input_template": "Write a regression test that:\n1. Fails WITHOUT the fix (proves the bug existed)\n2. Passes WITH the fix (proves the fix works)\n3. Covers edge cases identified in investigation\n\nContext:\n{{handoff}}\n\nOutput TESTS_PASS when regression test passes.",
39
+ "expects": "TESTS_PASS",
40
+ "retry": 2
41
+ },
42
+ {
43
+ "id": "verify",
44
+ "label": "Independent Verification",
45
+ "agent_hint": "architect",
46
+ "agent_override": "merlin-verifier",
47
+ "input_template": "Verify this bug fix cold. Check:\n- Fix addresses root cause (not just symptom)\n- No regressions introduced\n- Edge cases handled\n- Test coverage adequate\n\nContext:\n{{handoff}}\n\nOutput VERIFIED when review passes.",
48
+ "expects": "VERIFIED",
49
+ "independent": true,
50
+ "retry": 1
51
+ },
52
+ {
53
+ "id": "pr",
54
+ "label": "Pull Request",
55
+ "agent_hint": "impl",
56
+ "input_template": "Create a PR for this bug fix.\n\nContext:\n{{handoff}}\n\nPR should reference the bug, explain root cause, and describe the fix. Include before/after behavior.\n\nOutput PR_CREATED when done.",
57
+ "expects": "PR_CREATED",
58
+ "action": "gh_pr_create",
59
+ "retry": 1
60
+ }
61
+ ],
62
+ "on_failure": "pause",
63
+ "on_complete": "notify"
64
+ }
@@ -0,0 +1,73 @@
1
+ {
2
+ "id": "feature-dev",
3
+ "name": "Feature Development",
4
+ "description": "Drop in a feature request. Get back a tested PR.",
5
+ "version": "1.0",
6
+ "steps": [
7
+ {
8
+ "id": "plan",
9
+ "label": "Planning",
10
+ "agent_hint": "spec",
11
+ "agent_override": "merlin-planner",
12
+ "input_template": "Create a detailed implementation plan for: {{task}}\n\nInclude: file changes, acceptance criteria, edge cases, and testing strategy.\n\nOutput PLAN_COMPLETE when done.",
13
+ "expects": "PLAN_COMPLETE",
14
+ "output_file": "plan.md",
15
+ "retry": 2
16
+ },
17
+ {
18
+ "id": "setup",
19
+ "label": "Environment Setup",
20
+ "agent_hint": "architect",
21
+ "input_template": "Set up environment and scaffolding for this plan:\n\n{{plan.md}}\n\nCreate any new files, install dependencies, set up config.\nHandoff context:\n{{handoff}}\n\nOutput SETUP_COMPLETE when done.",
22
+ "expects": "SETUP_COMPLETE",
23
+ "retry": 1
24
+ },
25
+ {
26
+ "id": "implement",
27
+ "label": "Implementation",
28
+ "agent_hint": "impl",
29
+ "input_template": "Implement according to this plan:\n\n{{plan.md}}\n\nPrevious context:\n{{handoff}}\n\nFollow existing patterns. Keep files under 400 lines. Commit after each logical change.\n\nOutput IMPL_COMPLETE when done.",
30
+ "expects": "IMPL_COMPLETE",
31
+ "retry": 3,
32
+ "blend": true
33
+ },
34
+ {
35
+ "id": "verify",
36
+ "label": "Independent Verification",
37
+ "agent_hint": "architect",
38
+ "agent_override": "merlin-verifier",
39
+ "input_template": "Review this implementation cold. Do not read the original task. Only evaluate the code quality, correctness, and completeness based on:\n\n{{handoff}}\n\nCheck files changed, look for bugs, missing error handling, security issues.\n\nOutput VERIFIED when review is complete.",
40
+ "expects": "VERIFIED",
41
+ "independent": true,
42
+ "retry": 1
43
+ },
44
+ {
45
+ "id": "test",
46
+ "label": "Testing",
47
+ "agent_hint": "test",
48
+ "input_template": "Write and run tests for the work described in:\n\n{{handoff}}\n\nWrite unit tests for core logic. Write integration tests for API endpoints. Ensure all tests pass.\n\nOutput TESTS_PASS when all tests pass.",
49
+ "expects": "TESTS_PASS",
50
+ "retry": 2
51
+ },
52
+ {
53
+ "id": "pr",
54
+ "label": "Pull Request",
55
+ "agent_hint": "impl",
56
+ "input_template": "Create a PR for the completed work.\n\nContext:\n{{handoff}}\n\nWrite a clear PR title and description summarizing all changes. Use `gh pr create`.\n\nOutput PR_CREATED when the PR is created.",
57
+ "expects": "PR_CREATED",
58
+ "action": "gh_pr_create",
59
+ "retry": 1
60
+ },
61
+ {
62
+ "id": "review",
63
+ "label": "Final Review",
64
+ "agent_hint": "spec",
65
+ "agent_override": "merlin-milestone-auditor",
66
+ "input_template": "Audit the completed work. Verify all acceptance criteria from the original plan are met.\n\nContext:\n{{handoff}}\n\nOriginal task: {{task}}\n\nCheck each criterion against actual code. Report gaps if any.\n\nOutput APPROVED if all criteria met.",
67
+ "expects": "APPROVED",
68
+ "retry": 1
69
+ }
70
+ ],
71
+ "on_failure": "pause",
72
+ "on_complete": "notify"
73
+ }
@@ -0,0 +1,64 @@
1
+ {
2
+ "id": "refactor",
3
+ "name": "Code Refactor",
4
+ "description": "Analyze, plan, refactor, verify, test, and PR. Clean code pipeline.",
5
+ "version": "1.0",
6
+ "steps": [
7
+ {
8
+ "id": "analyze",
9
+ "label": "Code Analysis",
10
+ "agent_hint": "refactor",
11
+ "input_template": "Analyze the codebase for refactoring opportunities:\n\n{{task}}\n\nLook for:\n- Files over 400 lines\n- Duplicated code patterns\n- God objects / functions doing too much\n- Circular dependencies\n- Dead code\n- Inconsistent patterns\n- Missing abstractions\n\nProduce a detailed analysis with specific file paths and line counts.\n\nOutput ANALYSIS_COMPLETE with findings.",
12
+ "expects": "ANALYSIS_COMPLETE",
13
+ "output_file": "analysis.md",
14
+ "retry": 1
15
+ },
16
+ {
17
+ "id": "plan",
18
+ "label": "Refactor Plan",
19
+ "agent_hint": "architect",
20
+ "input_template": "Create a safe refactoring plan based on analysis:\n\n{{analysis.md}}\n\nPrevious context:\n{{handoff}}\n\nFor each refactoring:\n1. What changes (specific files and functions)\n2. Why it improves the code\n3. Risk level (low/medium/high)\n4. Order (safest first, riskiest last)\n\nEnsure each step is atomic and independently testable.\n\nOutput PLAN_COMPLETE with ordered refactoring steps.",
21
+ "expects": "PLAN_COMPLETE",
22
+ "output_file": "refactor-plan.md",
23
+ "retry": 1
24
+ },
25
+ {
26
+ "id": "refactor",
27
+ "label": "Apply Refactoring",
28
+ "agent_hint": "refactor",
29
+ "input_template": "Execute the refactoring plan:\n\n{{refactor-plan.md}}\n\nPrevious context:\n{{handoff}}\n\nRules:\n- One atomic commit per refactoring step\n- Run existing tests after each step\n- If tests break, fix before moving on\n- Keep files under 400 lines\n- Preserve all existing behavior\n\nOutput REFACTOR_COMPLETE when all steps done.",
30
+ "expects": "REFACTOR_COMPLETE",
31
+ "retry": 3,
32
+ "blend": true
33
+ },
34
+ {
35
+ "id": "verify",
36
+ "label": "Behavior Verification",
37
+ "agent_hint": "architect",
38
+ "agent_override": "merlin-verifier",
39
+ "input_template": "Verify that refactoring preserved all behavior.\n\nReview the changes cold — do not read the refactor plan.\nCheck:\n- No behavior changes (only structural)\n- All public APIs unchanged\n- No missing imports or broken references\n- Tests still pass\n\nContext:\n{{handoff}}\n\nOutput VERIFIED if behavior is preserved.",
40
+ "expects": "VERIFIED",
41
+ "independent": true,
42
+ "retry": 1
43
+ },
44
+ {
45
+ "id": "test",
46
+ "label": "Test Suite",
47
+ "agent_hint": "test",
48
+ "input_template": "Run the full test suite and add tests for any untested refactored code:\n\nContext:\n{{handoff}}\n\n1. Run all existing tests — they MUST pass\n2. Add tests for newly extracted functions/modules\n3. Verify test coverage didn't decrease\n\nOutput TESTS_PASS when all tests pass.",
49
+ "expects": "TESTS_PASS",
50
+ "retry": 2
51
+ },
52
+ {
53
+ "id": "pr",
54
+ "label": "Refactor PR",
55
+ "agent_hint": "impl",
56
+ "input_template": "Create a PR for the refactoring work.\n\nContext:\n{{handoff}}\n\nPR should explain:\n- What was refactored and why\n- Key structural changes\n- That all tests pass and behavior is preserved\n\nOutput PR_CREATED when done.",
57
+ "expects": "PR_CREATED",
58
+ "action": "gh_pr_create",
59
+ "retry": 1
60
+ }
61
+ ],
62
+ "on_failure": "pause",
63
+ "on_complete": "notify"
64
+ }