acpx-team 0.2.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,521 @@
1
+ #!/usr/bin/env bash
2
+ # protocols.sh — Protocol implementations with Plan-First flow
3
+ # Each protocol: Phase 1 (Plan) → consensus check → Phase 2 (Execute)
4
+ # Compatible with Bash 3.2+ (no mapfile, no associative arrays)
5
+
6
+ set -euo pipefail
7
+
8
+ ACPX_ROOT="${ACPX_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
9
+ ACPX_WORKSPACE="${ACPX_WORKSPACE:-.acpx-workspace}"
10
+
11
+ source "${ACPX_ROOT}/lib/workspace.sh"
12
+ source "${ACPX_ROOT}/lib/roles.sh"
13
+ source "${ACPX_ROOT}/lib/synthesize.sh"
14
+
15
+ # ─── Portable Helpers ──────────────────────────────────────────
16
+ # read_into_array VARNAME < input (replaces mapfile for Bash 3.2)
17
+
18
+ read_into_array() {
19
+ local _varname="$1"
20
+ eval "${_varname}=()"
21
+ while IFS= read -r _line; do
22
+ [[ -n "$_line" ]] && eval "${_varname}+=(\"\$_line\")"
23
+ done
24
+ }
25
+
26
+ # ─── Agent Detection ───────────────────────────────────────────
27
+
28
+ _detect_available_agents() {
29
+ local found=""
30
+ for cmd in claude codex gemini opencode cursor copilot pi qwen openclaw; do
31
+ if command -v "acpx-${cmd}" &>/dev/null 2>&1 || acpx "${cmd}" exec "echo ok" &>/dev/null 2>&1; then
32
+ found="${found}${cmd}"$'\n'
33
+ fi
34
+ done
35
+ printf '%s' "$found"
36
+ }
37
+
38
+ _get_agents_or_default() {
39
+ local agents_spec="${1:-auto}"
40
+ if [[ "$agents_spec" == "auto" ]]; then
41
+ local detected
42
+ detected=$(_detect_available_agents)
43
+ if [[ -z "$detected" ]]; then
44
+ echo "claude"
45
+ else
46
+ printf '%s' "$detected"
47
+ fi
48
+ else
49
+ echo "$agents_spec" | tr ',' '\n'
50
+ fi
51
+ }
52
+
53
+ _run_agent_plan() {
54
+ local agent="$1"
55
+ local session="$2"
56
+ local prompt="$3"
57
+
58
+ acpx "$agent" -s "$session" set-mode plan 2>/dev/null || true
59
+ acpx --format quiet "$agent" -s "$session" "$prompt"
60
+ }
61
+
62
+ _run_agent_execute() {
63
+ local agent="$1"
64
+ local session="$2"
65
+ local prompt="$3"
66
+ local mode="${4:-acceptEdits}"
67
+
68
+ acpx "$agent" -s "$session" set-mode "$mode" 2>/dev/null || true
69
+ acpx --format quiet "$agent" -s "$session" "$prompt"
70
+ }
71
+
72
+ # ─── Protocol 1: Parallel Fan-Out ──────────────────────────────
73
+
74
+ protocol_fanout() {
75
+ local task="${1:?Usage: protocol_fanout <task> [agents] [orchestrator]}"
76
+ local agents_spec="${2:-auto}"
77
+ local orchestrator="${3:-claude}"
78
+
79
+ local -a agents=()
80
+ while IFS= read -r line; do
81
+ [[ -n "$line" ]] && agents+=("$line")
82
+ done < <(_get_agents_or_default "$agents_spec")
83
+
84
+ workspace_init "$task" "fanout"
85
+
86
+ # ── Phase 1: Plan (parallel fan-out) ──
87
+ workspace_set_phase "plan"
88
+ workspace_set_round 1
89
+
90
+ echo "==> Protocol 1: Fan-Out | ${#agents[@]} agent(s) | Plan phase"
91
+
92
+ local pids=()
93
+ for agent in "${agents[@]}"; do
94
+ local session="fanout-${agent}"
95
+ acpx "${agent}" sessions new --name "$session" 2>/dev/null || true
96
+
97
+ _run_agent_plan "$agent" "$session" "$task" \
98
+ | workspace_write_agent_output "$agent" 1 &
99
+ pids+=($!)
100
+ done
101
+
102
+ for pid in "${pids[@]}"; do
103
+ wait "$pid" 2>/dev/null || true
104
+ done
105
+
106
+ # ── Synthesize ──
107
+ echo "==> Synthesizing..."
108
+ synthesize_round 1 "$orchestrator"
109
+
110
+ workspace_set_phase "done"
111
+ echo "==> Fan-Out complete. See $ACPX_WORKSPACE/synthesis.md"
112
+
113
+ for agent in "${agents[@]}"; do
114
+ acpx "${agent}" sessions close "fanout-${agent}" 2>/dev/null || true
115
+ done
116
+ }
117
+
118
+ # ─── Protocol 2: Round-Robin Deliberation ──────────────────────
119
+
120
+ protocol_deliberation() {
121
+ local task="${1:?Usage: protocol_deliberation <task> [agents] [orchestrator]}"
122
+ local agents_spec="${2:-auto}"
123
+ local orchestrator="${3:-claude}"
124
+
125
+ local -a agents=()
126
+ while IFS= read -r line; do
127
+ [[ -n "$line" ]] && agents+=("$line")
128
+ done < <(_get_agents_or_default "$agents_spec")
129
+
130
+ workspace_init "$task" "deliberation"
131
+
132
+ # ── Phase 1: Plan - Round 1 ──
133
+ workspace_set_phase "plan"
134
+ workspace_set_round 1
135
+
136
+ echo "==> Protocol 2: Deliberation | ${#agents[@]} agent(s) | Round 1 (Plan)"
137
+
138
+ local pids=()
139
+ for agent in "${agents[@]}"; do
140
+ local session="delib-${agent}"
141
+ acpx "${agent}" sessions new --name "$session" 2>/dev/null || true
142
+
143
+ _run_agent_plan "$agent" "$session" "$task" \
144
+ | workspace_write_agent_output "$agent" 1 &
145
+ pids+=($!)
146
+ done
147
+ for pid in "${pids[@]}"; do wait "$pid" 2>/dev/null || true; done
148
+
149
+ # ── Consensus check ──
150
+ local consensus
151
+ consensus=$(consensus_check 1 "$orchestrator")
152
+ echo "==> Round 1 consensus: ${consensus}"
153
+
154
+ if [[ "$consensus" == "HIGH" ]]; then
155
+ echo "==> High consensus after Round 1 — skipping Round 2"
156
+ synthesize_round 1 "$orchestrator"
157
+ synthesize_plan "$orchestrator"
158
+ workspace_set_phase "done"
159
+ echo "==> Deliberation complete. See $ACPX_WORKSPACE/synthesis.md"
160
+ return
161
+ fi
162
+
163
+ # ── Round 2: Deliberation ──
164
+ workspace_set_round 2
165
+ echo "==> Round 2 (Deliberation)"
166
+
167
+ local all_r1
168
+ all_r1=$(workspace_gather_round 1)
169
+
170
+ pids=()
171
+ for agent in "${agents[@]}"; do
172
+ local session="delib-${agent}"
173
+ local r2_prompt="[Round 2: Deliberation]
174
+ Other reviewers provided their analysis below. Consider their points fairly.
175
+ Update your analysis where you find their arguments convincing. Note any remaining disagreements.
176
+
177
+ ${all_r1}"
178
+
179
+ _run_agent_plan "$agent" "$session" "$r2_prompt" \
180
+ | workspace_write_agent_output "$agent" 2 &
181
+ pids+=($!)
182
+ done
183
+ for pid in "${pids[@]}"; do wait "$pid" 2>/dev/null || true; done
184
+
185
+ # ── Synthesize ──
186
+ echo "==> Synthesizing..."
187
+ synthesize_round 2 "$orchestrator"
188
+ synthesize_plan "$orchestrator"
189
+
190
+ workspace_set_phase "execute"
191
+ echo "==> Plan phase complete. Ready to execute."
192
+ echo "==> Run: acpx-council execute --from-workspace"
193
+
194
+ for agent in "${agents[@]}"; do
195
+ acpx "${agent}" sessions close "delib-${agent}" 2>/dev/null || true
196
+ done
197
+ }
198
+
199
+ # ─── Protocol 3: Role-Specialized Council (Recommended) ────────
200
+
201
+ protocol_role_council() {
202
+ local task="${1:?Usage: protocol_role_council <task> [agents] [roles] [orchestrator]}"
203
+ local agents_spec="${2:-auto}"
204
+ local roles_spec="${3:-auto}"
205
+ local orchestrator="${4:-claude}"
206
+
207
+ local -a agents=()
208
+ while IFS= read -r line; do
209
+ [[ -n "$line" ]] && agents+=("$line")
210
+ done < <(_get_agents_or_default "$agents_spec")
211
+
212
+ # Infer roles if auto
213
+ local -a roles=()
214
+ if [[ "$roles_spec" == "auto" ]]; then
215
+ while IFS= read -r line; do
216
+ [[ -n "$line" ]] && roles+=("$line")
217
+ done < <(role_infer_from_task "$task")
218
+ # Ensure at least as many roles as agents
219
+ while [[ ${#roles[@]} -lt ${#agents[@]} ]]; do
220
+ roles+=("neutral")
221
+ done
222
+ else
223
+ while IFS= read -r line; do
224
+ [[ -n "$line" ]] && roles+=("$line")
225
+ done < <(echo "$roles_spec" | tr ',' '\n')
226
+ fi
227
+
228
+ workspace_init "$task" "role-council"
229
+
230
+ # ── Phase 1: Plan - Round 1 with roles ──
231
+ workspace_set_phase "plan"
232
+ workspace_set_round 1
233
+
234
+ echo "==> Protocol 3: Role Council | ${#agents[@]} agent(s)"
235
+ echo " Agents: ${agents[*]}"
236
+ echo " Roles: ${roles[*]}"
237
+
238
+ local pids=()
239
+ local i=0
240
+ for agent in "${agents[@]}"; do
241
+ local role="${roles[$i]:-neutral}"
242
+ local session="council-${agent}"
243
+
244
+ acpx "${agent}" sessions new --name "$session" 2>/dev/null || true
245
+
246
+ local r1_prompt
247
+ r1_prompt="$(role_get_r1 "$role")
248
+
249
+ ${task}"
250
+
251
+ _run_agent_plan "$agent" "$session" "$r1_prompt" \
252
+ | workspace_write_agent_output "$agent" 1 &
253
+ pids+=($!)
254
+ i=$((i + 1))
255
+ done
256
+ for pid in "${pids[@]}"; do wait "$pid" 2>/dev/null || true; done
257
+
258
+ # ── Consensus check ──
259
+ local consensus
260
+ consensus=$(consensus_check 1 "$orchestrator")
261
+ echo "==> Round 1 consensus: ${consensus}"
262
+
263
+ if [[ "$consensus" == "HIGH" ]]; then
264
+ echo "==> High consensus — synthesizing plan"
265
+ synthesize_round 1 "$orchestrator"
266
+ synthesize_plan "$orchestrator"
267
+ workspace_set_phase "execute"
268
+ echo "==> Plan ready. See $ACPX_WORKSPACE/plan.md"
269
+ return
270
+ fi
271
+
272
+ # ── Round 2: Role-persistent deliberation ──
273
+ workspace_set_round 2
274
+ echo "==> Round 2 (Role Deliberation)"
275
+
276
+ local all_r1
277
+ all_r1=$(workspace_gather_round 1)
278
+
279
+ pids=()
280
+ i=0
281
+ for agent in "${agents[@]}"; do
282
+ local role="${roles[$i]:-neutral}"
283
+ local session="council-${agent}"
284
+
285
+ local r2_prompt
286
+ r2_prompt="$(role_get_r2 "$role")
287
+
288
+ Other experts' analysis:
289
+ ${all_r1}"
290
+
291
+ _run_agent_plan "$agent" "$session" "$r2_prompt" \
292
+ | workspace_write_agent_output "$agent" 2 &
293
+ pids+=($!)
294
+ i=$((i + 1))
295
+ done
296
+ for pid in "${pids[@]}"; do wait "$pid" 2>/dev/null || true; done
297
+
298
+ # ── Synthesize ──
299
+ echo "==> Synthesizing..."
300
+ synthesize_round 2 "$orchestrator"
301
+ synthesize_plan "$orchestrator"
302
+
303
+ workspace_set_phase "execute"
304
+ echo "==> Plan ready. See $ACPX_WORKSPACE/plan.md"
305
+
306
+ for agent in "${agents[@]}"; do
307
+ acpx "${agent}" sessions close "council-${agent}" 2>/dev/null || true
308
+ done
309
+ }
310
+
311
+ # ─── Protocol 4: Adversarial Debate ────────────────────────────
312
+
313
+ protocol_adversarial() {
314
+ local task="${1:?Usage: protocol_adversarial <task> [agents] [orchestrator]}"
315
+ local agents_spec="${2:-claude,codex}"
316
+ local orchestrator="${3:-gemini}"
317
+
318
+ local -a agents=()
319
+ while IFS= read -r line; do
320
+ [[ -n "$line" ]] && agents+=("$line")
321
+ done < <(_get_agents_or_default "$agents_spec")
322
+
323
+ local advocate="${agents[0]:-claude}"
324
+ local critic="${agents[1]:-codex}"
325
+ local judge="${agents[2]:-$orchestrator}"
326
+
327
+ workspace_init "$task" "adversarial"
328
+ workspace_set_phase "plan"
329
+ workspace_set_round 1
330
+
331
+ echo "==> Protocol 4: Adversarial Debate"
332
+ echo " Advocate: ${advocate} | Critic: ${critic} | Judge: ${judge}"
333
+
334
+ acpx "$advocate" sessions new --name "bull" 2>/dev/null || true
335
+ acpx "$critic" sessions new --name "bear" 2>/dev/null || true
336
+ acpx "$judge" sessions new --name "judge" 2>/dev/null || true
337
+
338
+ # Round 1: Opening arguments
339
+ echo "==> Opening arguments..."
340
+
341
+ _run_agent_plan "$advocate" "bull" "Argue FOR the following proposal. Provide specific technical benefits and evidence.
342
+ ${task}" | workspace_write_agent_output "advocate" 1 &
343
+ local pid1=$!
344
+
345
+ _run_agent_plan "$critic" "bear" "Argue AGAINST the following proposal. Provide specific technical risks and counter-evidence.
346
+ ${task}" | workspace_write_agent_output "critic" 1 &
347
+ local pid2=$!
348
+
349
+ wait "$pid1" 2>/dev/null || true
350
+ wait "$pid2" 2>/dev/null || true
351
+
352
+ # Round 2: Cross-arguments
353
+ workspace_set_round 2
354
+ echo "==> Cross-arguments..."
355
+
356
+ local bull_r1 critic_r1
357
+ bull_r1=$(workspace_read_agent_output "advocate" 1)
358
+ critic_r1=$(workspace_read_agent_output "critic" 1)
359
+
360
+ _run_agent_plan "$advocate" "bull" "The critic argues:
361
+ ${critic_r1}
362
+
363
+ Counter-argue. Address each concern specifically." | workspace_write_agent_output "advocate" 2 &
364
+ pid1=$!
365
+
366
+ _run_agent_plan "$critic" "bear" "The advocate argues:
367
+ ${bull_r1}
368
+
369
+ Counter-argue. Address each claim specifically." | workspace_write_agent_output "critic" 2 &
370
+ pid2=$!
371
+
372
+ wait "$pid1" 2>/dev/null || true
373
+ wait "$pid2" 2>/dev/null || true
374
+
375
+ # Judge synthesis
376
+ echo "==> Judge synthesizing..."
377
+
378
+ local bull_r2 critic_r2
379
+ bull_r2=$(workspace_read_agent_output "advocate" 2)
380
+ critic_r2=$(workspace_read_agent_output "critic" 2)
381
+
382
+ acpx --format quiet "$judge" -s judge "You are the judge. Synthesize this debate into a final recommendation.
383
+
384
+ [FOR]:
385
+ ${bull_r2}
386
+
387
+ [AGAINST]:
388
+ ${critic_r2}
389
+
390
+ Provide:
391
+ 1. Summary of key arguments on each side
392
+ 2. Points of agreement
393
+ 3. Unresolved tensions
394
+ 4. Your final recommendation with confidence level (HIGH/MEDIUM/LOW)" \
395
+ | workspace_write_agent_output "judge" 1
396
+
397
+ cp "$ACPX_WORKSPACE/agents/judge/round-1.md" "$ACPX_WORKSPACE/synthesis.md"
398
+
399
+ synthesize_plan "$orchestrator"
400
+ workspace_set_phase "execute"
401
+ echo "==> Debate complete. See $ACPX_WORKSPACE/synthesis.md"
402
+
403
+ acpx "$advocate" sessions close "bull" 2>/dev/null || true
404
+ acpx "$critic" sessions close "bear" 2>/dev/null || true
405
+ acpx "$judge" sessions close "judge" 2>/dev/null || true
406
+ }
407
+
408
+ # ─── Protocol 5: Sequential Pipeline ───────────────────────────
409
+
410
+ protocol_pipeline() {
411
+ local task="${1:?Usage: protocol_pipeline <task> [agents] [orchestrator]}"
412
+ local agents_spec="${2:-claude,codex,claude}"
413
+ local orchestrator="${3:-claude}"
414
+
415
+ local -a agents=()
416
+ while IFS= read -r line; do
417
+ [[ -n "$line" ]] && agents+=("$line")
418
+ done < <(_get_agents_or_default "$agents_spec")
419
+
420
+ local writer="${agents[0]:-claude}"
421
+ local reviewer="${agents[1]:-codex}"
422
+ local editor="${agents[2]:-$writer}"
423
+
424
+ workspace_init "$task" "pipeline"
425
+ workspace_set_phase "plan"
426
+
427
+ echo "==> Protocol 5: Pipeline"
428
+ echo " Writer: ${writer} → Reviewer: ${reviewer} → Editor: ${editor}"
429
+
430
+ # Step 1: Plan (writer)
431
+ echo "==> [Plan] Writer analyzing..."
432
+ acpx "$writer" sessions new --name "pipe-writer" 2>/dev/null || true
433
+ _run_agent_plan "$writer" "pipe-writer" "$task" \
434
+ | workspace_write_agent_output "writer" 1
435
+
436
+ # Step 2: Review
437
+ echo "==> [Plan] Reviewer checking..."
438
+ acpx "$reviewer" sessions new --name "pipe-reviewer" 2>/dev/null || true
439
+ local writer_output
440
+ writer_output=$(workspace_read_agent_output "writer" 1)
441
+
442
+ _run_agent_plan "$reviewer" "pipe-reviewer" "Review this analysis for gaps, errors, and edge cases:
443
+ ${writer_output}
444
+
445
+ Rate each finding: CRITICAL/HIGH/MEDIUM/LOW." | workspace_write_agent_output "reviewer" 1
446
+
447
+ # Step 3: Edit
448
+ echo "==> [Plan] Editor revising..."
449
+ local review_output
450
+ review_output=$(workspace_read_agent_output "reviewer" 1)
451
+
452
+ _run_agent_plan "$writer" "pipe-writer" "Incorporate this review feedback into your original analysis:
453
+ ${review_output}
454
+
455
+ Original output:
456
+ ${writer_output}" | workspace_write_agent_output "editor" 1
457
+
458
+ synthesize_round 1 "$orchestrator"
459
+ synthesize_plan "$orchestrator"
460
+
461
+ workspace_set_phase "execute"
462
+ echo "==> Pipeline complete. See $ACPX_WORKSPACE/synthesis.md"
463
+
464
+ acpx "$writer" sessions close "pipe-writer" 2>/dev/null || true
465
+ acpx "$reviewer" sessions close "pipe-reviewer" 2>/dev/null || true
466
+ }
467
+
468
+ # ─── Protocol Auto-Select ──────────────────────────────────────
469
+
470
+ protocol_auto_select() {
471
+ local task="${1:?Usage: protocol_auto_select <task>}"
472
+ local task_lower
473
+ task_lower=$(echo "$task" | tr '[:upper:]' '[:lower:]')
474
+
475
+ case "$task_lower" in
476
+ *review*|*audit*|*assess*) echo "role-council" ;;
477
+ *should*|*decide*|*choose*|*whether*) echo "adversarial" ;;
478
+ *implement*|*build*|*create*|*add*) echo "role-council" ;;
479
+ *quick*|*opinion*|*think*) echo "fanout" ;;
480
+ *design*|*architect*|*plan*) echo "role-council" ;;
481
+ *debug*|*fix*|*investigate*) echo "pipeline" ;;
482
+ *) echo "role-council" ;;
483
+ esac
484
+ }
485
+
486
+ # ─── Execute Phase ─────────────────────────────────────────────
487
+
488
+ protocol_execute() {
489
+ local plan_file="${1:-$ACPX_WORKSPACE/plan.md}"
490
+ local agents_spec="${2:-auto}"
491
+ local orchestrator="${3:-claude}"
492
+
493
+ if [[ ! -f "$plan_file" ]]; then
494
+ echo "Error: No plan found at $plan_file. Run plan phase first." >&2
495
+ return 1
496
+ fi
497
+
498
+ local plan
499
+ plan=$(cat "$plan_file")
500
+
501
+ local -a agents=()
502
+ while IFS= read -r line; do
503
+ [[ -n "$line" ]] && agents+=("$line")
504
+ done < <(_get_agents_or_default "$agents_spec")
505
+
506
+ workspace_set_phase "execute"
507
+ echo "==> Executing plan with ${#agents[@]} agent(s)"
508
+
509
+ local primary="${agents[0]}"
510
+ local session="exec-${primary}"
511
+
512
+ acpx "$primary" sessions new --name "$session" 2>/dev/null || true
513
+
514
+ _run_agent_execute "$primary" "$session" "Execute this plan. Follow each step in order:
515
+ ${plan}" "acceptEdits" | workspace_write_agent_output "executor" 1
516
+
517
+ echo "==> Execution complete. See $ACPX_WORKSPACE/agents/executor/round-1.md"
518
+
519
+ acpx "$primary" sessions close "$session" 2>/dev/null || true
520
+ workspace_set_phase "review"
521
+ }