ai-consultants 2.10.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/.claude/commands/ai-consultants:consult.md +94 -0
- package/.claude/commands/ai-consultants:debate.md +103 -0
- package/.claude/commands/ai-consultants:help.md +30 -0
- package/CLAUDE.md +839 -0
- package/LICENSE +21 -0
- package/README.md +758 -0
- package/SKILL.md +156 -0
- package/bin/ai-consultants +96 -0
- package/docs/COST_RATES.md +172 -0
- package/docs/JSON_SCHEMA.md +305 -0
- package/docs/SETUP.md +879 -0
- package/docs/SMART_ROUTING.md +219 -0
- package/docs/cost_rates.json +143 -0
- package/package.json +47 -0
- package/references/configuration.md +141 -0
- package/references/details.md +60 -0
- package/scripts/build_context.sh +562 -0
- package/scripts/classify_question.sh +148 -0
- package/scripts/config.sh +753 -0
- package/scripts/configure.sh +1093 -0
- package/scripts/consult_all.sh +934 -0
- package/scripts/debate_round.sh +421 -0
- package/scripts/doctor.sh +642 -0
- package/scripts/followup.sh +197 -0
- package/scripts/install.sh +319 -0
- package/scripts/lib/api.sh +640 -0
- package/scripts/lib/api_query.sh +228 -0
- package/scripts/lib/cache.sh +338 -0
- package/scripts/lib/chunking.sh +880 -0
- package/scripts/lib/code_optimizer.sh +671 -0
- package/scripts/lib/common.sh +1088 -0
- package/scripts/lib/costs.sh +674 -0
- package/scripts/lib/personas.sh +372 -0
- package/scripts/lib/progress.sh +251 -0
- package/scripts/lib/reflection.sh +447 -0
- package/scripts/lib/routing.sh +566 -0
- package/scripts/lib/schema.json +165 -0
- package/scripts/lib/session.sh +327 -0
- package/scripts/lib/symbol_map.sh +445 -0
- package/scripts/lib/voting.sh +495 -0
- package/scripts/peer_review.sh +460 -0
- package/scripts/preflight_check.sh +452 -0
- package/scripts/query_aider.sh +83 -0
- package/scripts/query_amp.sh +67 -0
- package/scripts/query_claude.sh +99 -0
- package/scripts/query_codex.sh +92 -0
- package/scripts/query_cursor.sh +72 -0
- package/scripts/query_deepseek.sh +17 -0
- package/scripts/query_gemini.sh +85 -0
- package/scripts/query_glm.sh +17 -0
- package/scripts/query_grok.sh +17 -0
- package/scripts/query_kilo.sh +140 -0
- package/scripts/query_kimi.sh +67 -0
- package/scripts/query_minimax.sh +17 -0
- package/scripts/query_mistral.sh +97 -0
- package/scripts/query_ollama.sh +328 -0
- package/scripts/query_qwen3.sh +93 -0
- package/scripts/setup_wizard.sh +367 -0
- package/scripts/skillport-install.sh +196 -0
- package/scripts/synthesize.sh +332 -0
- package/templates/consultation_report.md +323 -0
- package/templates/synthesis_prompt.md +120 -0
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# debate_round.sh - Multi-Agent Debate (MAD) implementation for AI Consultants
|
|
3
|
+
#
|
|
4
|
+
# Implements deliberation rounds where consultants can see and
|
|
5
|
+
# respond to each other's responses.
|
|
6
|
+
#
|
|
7
|
+
# Usage: ./debate_round.sh <responses_dir> <round_number> <output_dir>
|
|
8
|
+
#
|
|
9
|
+
# Round 1: Initial responses (handled by consult_all.sh)
|
|
10
|
+
# Round 2: Cross-critique - each consultant sees other responses
|
|
11
|
+
# Round 3: Final stance - final position after seeing critiques
|
|
12
|
+
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
16
|
+
source "$SCRIPT_DIR/lib/common.sh"
|
|
17
|
+
source "$SCRIPT_DIR/lib/personas.sh"
|
|
18
|
+
source "$SCRIPT_DIR/config.sh" 2>/dev/null || log_warn "config.sh not loaded (using defaults)"
|
|
19
|
+
|
|
20
|
+
# =============================================================================
|
|
21
|
+
# DEBATE OPTIMIZATION (v2.3)
|
|
22
|
+
# =============================================================================
|
|
23
|
+
|
|
24
|
+
# Check if category requires mandatory debate (v2.3 Quality Review)
|
|
25
|
+
# Usage: is_mandatory_debate_category <category>
|
|
26
|
+
is_mandatory_debate_category() {
|
|
27
|
+
local category="${1:-GENERAL}"
|
|
28
|
+
case "$category" in
|
|
29
|
+
SECURITY|ARCHITECTURE)
|
|
30
|
+
return 0 # Mandatory debate
|
|
31
|
+
;;
|
|
32
|
+
*)
|
|
33
|
+
return 1 # Not mandatory
|
|
34
|
+
;;
|
|
35
|
+
esac
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
# Check if debate should be skipped based on confidence spread
|
|
39
|
+
# Usage: should_skip_debate <responses_dir> [category]
|
|
40
|
+
should_skip_debate() {
|
|
41
|
+
local responses_dir="$1"
|
|
42
|
+
local category="${2:-GENERAL}"
|
|
43
|
+
|
|
44
|
+
# SECURITY and ARCHITECTURE always require debate (v2.3 Quality Review)
|
|
45
|
+
if is_mandatory_debate_category "$category"; then
|
|
46
|
+
return 1 # Don't skip - mandatory categories always debate
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# Skip if debate optimization is not enabled
|
|
50
|
+
if [[ "${ENABLE_DEBATE_OPTIMIZATION:-false}" != "true" ]]; then
|
|
51
|
+
return 1 # Don't skip
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
local threshold="${DEBATE_CONFIDENCE_SPREAD_THRESHOLD:-3}"
|
|
55
|
+
local min_confidence=10
|
|
56
|
+
local max_confidence=0
|
|
57
|
+
local count=0
|
|
58
|
+
|
|
59
|
+
for f in "$responses_dir"/*.json; do
|
|
60
|
+
if [[ -f "$f" && "$f" != *"summary"* && "$f" != *"round_"* ]]; then
|
|
61
|
+
local confidence
|
|
62
|
+
confidence=$(jq -r '.confidence.score // 5' "$f" 2>/dev/null)
|
|
63
|
+
if [[ "$confidence" =~ ^[0-9]+$ ]]; then
|
|
64
|
+
[[ $confidence -lt $min_confidence ]] && min_confidence=$confidence
|
|
65
|
+
[[ $confidence -gt $max_confidence ]] && max_confidence=$confidence
|
|
66
|
+
((count++)) || true
|
|
67
|
+
fi
|
|
68
|
+
fi
|
|
69
|
+
done
|
|
70
|
+
|
|
71
|
+
if [[ $count -lt 2 ]]; then
|
|
72
|
+
return 1 # Not enough responses to evaluate
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
local spread=$((max_confidence - min_confidence))
|
|
76
|
+
if [[ $spread -lt $threshold ]]; then
|
|
77
|
+
log_debug "Debate optimization: Skipping debate (confidence spread: $spread < threshold: $threshold)"
|
|
78
|
+
return 0 # Skip debate
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
return 1 # Don't skip
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# Extract compact summary for token-optimized debate
|
|
85
|
+
# Usage: extract_compact_summary <response_file>
|
|
86
|
+
extract_compact_summary() {
|
|
87
|
+
local response_file="$1"
|
|
88
|
+
|
|
89
|
+
if [[ "${DEBATE_USE_SUMMARIES:-true}" == "true" ]]; then
|
|
90
|
+
# Use only summary field (compact)
|
|
91
|
+
local consultant=$(jq -r '.consultant // "Unknown"' "$response_file" 2>/dev/null)
|
|
92
|
+
local summary=$(jq -r '.response.summary // "No summary"' "$response_file" 2>/dev/null | head -c 300)
|
|
93
|
+
local approach=$(jq -r '.response.approach // "unknown"' "$response_file" 2>/dev/null)
|
|
94
|
+
local confidence=$(jq -r '.confidence.score // 5' "$response_file" 2>/dev/null)
|
|
95
|
+
|
|
96
|
+
echo "**$consultant** ($approach, conf:$confidence): $summary"
|
|
97
|
+
else
|
|
98
|
+
# Use full extract_summary (original behavior)
|
|
99
|
+
extract_summary "$response_file"
|
|
100
|
+
fi
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# --- Parameters ---
|
|
104
|
+
RESPONSES_DIR="${1:-}"
|
|
105
|
+
ROUND_NUMBER="${2:-2}"
|
|
106
|
+
OUTPUT_DIR="${3:-}"
|
|
107
|
+
QUESTION_CATEGORY="${4:-GENERAL}" # v2.3: Category for mandatory debate check
|
|
108
|
+
|
|
109
|
+
if [[ ! "$ROUND_NUMBER" =~ ^[0-9]+$ ]] || [[ "$ROUND_NUMBER" -lt 1 ]]; then
|
|
110
|
+
log_error "Invalid round number: $ROUND_NUMBER (must be a positive integer)"
|
|
111
|
+
exit 1
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
if [[ -z "$RESPONSES_DIR" || ! -d "$RESPONSES_DIR" ]]; then
|
|
115
|
+
log_error "Usage: $0 <responses_dir> <round_number> [output_dir] [category]"
|
|
116
|
+
exit 1
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
if [[ -z "$OUTPUT_DIR" ]]; then
|
|
120
|
+
OUTPUT_DIR="$RESPONSES_DIR/round_${ROUND_NUMBER}"
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
mkdir -p "$OUTPUT_DIR"
|
|
124
|
+
|
|
125
|
+
log_info "=== Multi-Agent Debate - Round $ROUND_NUMBER ==="
|
|
126
|
+
|
|
127
|
+
# =============================================================================
|
|
128
|
+
# HELPER FUNCTIONS
|
|
129
|
+
# =============================================================================
|
|
130
|
+
|
|
131
|
+
# Extract summary from a response for the debate prompt
|
|
132
|
+
extract_summary() {
|
|
133
|
+
local response_file="$1"
|
|
134
|
+
local consultant=$(jq -r '.consultant // "Unknown"' "$response_file" 2>/dev/null)
|
|
135
|
+
local summary=$(jq -r '.response.summary // .response.detailed[:500] // "No summary"' "$response_file" 2>/dev/null)
|
|
136
|
+
local approach=$(jq -r '.response.approach // "unknown"' "$response_file" 2>/dev/null)
|
|
137
|
+
local confidence=$(jq -r '.confidence.score // 5' "$response_file" 2>/dev/null)
|
|
138
|
+
|
|
139
|
+
echo "**$consultant** (Approach: $approach, Confidence: $confidence/10):
|
|
140
|
+
$summary"
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
# Build prompt for debate round
|
|
144
|
+
build_debate_prompt() {
|
|
145
|
+
local consultant="$1"
|
|
146
|
+
local my_response_file="$2"
|
|
147
|
+
local responses_dir="$3"
|
|
148
|
+
|
|
149
|
+
local my_response=$(cat "$my_response_file" 2>/dev/null)
|
|
150
|
+
|
|
151
|
+
# Validate response JSON before using in debate prompt
|
|
152
|
+
if [[ -z "$my_response" ]] || ! echo "$my_response" | jq -e '.' > /dev/null; then
|
|
153
|
+
log_warn "Invalid or empty previous response for $consultant, using fallback"
|
|
154
|
+
my_response='{"response":{"summary":"No summary","approach":"unknown"},"confidence":{"score":5}}'
|
|
155
|
+
fi
|
|
156
|
+
|
|
157
|
+
local other_summaries=""
|
|
158
|
+
|
|
159
|
+
# Collect summaries from other consultants (using compact summaries when optimized)
|
|
160
|
+
for f in "$responses_dir"/*.json; do
|
|
161
|
+
if [[ -f "$f" && "$f" != *"summary"* && "$f" != *"round_"* ]]; then
|
|
162
|
+
local other_consultant=$(jq -r '.consultant // "Unknown"' "$f" 2>/dev/null)
|
|
163
|
+
if [[ "$other_consultant" != "$consultant" ]]; then
|
|
164
|
+
other_summaries+="
|
|
165
|
+
$(extract_compact_summary "$f")
|
|
166
|
+
---
|
|
167
|
+
"
|
|
168
|
+
fi
|
|
169
|
+
fi
|
|
170
|
+
done
|
|
171
|
+
|
|
172
|
+
cat << EOF
|
|
173
|
+
# Round $ROUND_NUMBER - Cross-Critique
|
|
174
|
+
|
|
175
|
+
## Your Previous Response (Round $((ROUND_NUMBER - 1)))
|
|
176
|
+
$(echo "$my_response" | jq -r '.response.summary // "No summary"' 2>/dev/null)
|
|
177
|
+
|
|
178
|
+
Approach: $(echo "$my_response" | jq -r '.response.approach // "unknown"' 2>/dev/null)
|
|
179
|
+
Confidence: $(echo "$my_response" | jq -r '.confidence.score // 5' 2>/dev/null)/10
|
|
180
|
+
|
|
181
|
+
## Other Consultants' Responses
|
|
182
|
+
$other_summaries
|
|
183
|
+
|
|
184
|
+
## Instructions
|
|
185
|
+
|
|
186
|
+
After seeing the opinions of other consultants, respond in JSON:
|
|
187
|
+
|
|
188
|
+
{
|
|
189
|
+
"debate": {
|
|
190
|
+
"round": $ROUND_NUMBER,
|
|
191
|
+
"position_changed": <true if you changed your mind, false otherwise>,
|
|
192
|
+
"original_stance": "<your original position>",
|
|
193
|
+
"updated_stance": "<your new position or confirmation of previous>",
|
|
194
|
+
"confidence_delta": <confidence change: -3 to +3>,
|
|
195
|
+
"critiques": [
|
|
196
|
+
{
|
|
197
|
+
"target": "<consultant name>",
|
|
198
|
+
"critique": "<your critique>",
|
|
199
|
+
"severity": "<minor|moderate|major>"
|
|
200
|
+
}
|
|
201
|
+
],
|
|
202
|
+
"incorporated_from": [
|
|
203
|
+
{
|
|
204
|
+
"source": "<consultant name>",
|
|
205
|
+
"idea": "<what you incorporated>"
|
|
206
|
+
}
|
|
207
|
+
],
|
|
208
|
+
"areas_of_agreement": ["<points you agree with others on>"],
|
|
209
|
+
"areas_of_disagreement": ["<points you disagree on>"]
|
|
210
|
+
},
|
|
211
|
+
"response": {
|
|
212
|
+
"summary": "<updated summary if position_changed, otherwise confirmation>",
|
|
213
|
+
"detailed": "<updated details>",
|
|
214
|
+
"approach": "<updated or confirmed approach>",
|
|
215
|
+
"pros": ["<updated pros>"],
|
|
216
|
+
"cons": ["<updated cons>"]
|
|
217
|
+
},
|
|
218
|
+
"confidence": {
|
|
219
|
+
"score": <new score 1-10>,
|
|
220
|
+
"reasoning": "<why this confidence level after debate>"
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
IMPORTANT: Respond ONLY with valid JSON. Be honest - if others have valid points, acknowledge them.
|
|
225
|
+
EOF
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
# =============================================================================
|
|
229
|
+
# MAIN DEBATE EXECUTION
|
|
230
|
+
# =============================================================================
|
|
231
|
+
|
|
232
|
+
run_debate_round() {
|
|
233
|
+
# Use shared function to get enabled consultants
|
|
234
|
+
local consultants_str
|
|
235
|
+
consultants_str=$(get_enabled_consultants)
|
|
236
|
+
# shellcheck disable=SC2206
|
|
237
|
+
local consultants=($consultants_str)
|
|
238
|
+
|
|
239
|
+
local pids=()
|
|
240
|
+
local output_files=()
|
|
241
|
+
local consultant_names=()
|
|
242
|
+
|
|
243
|
+
log_info "Starting debate round $ROUND_NUMBER for ${#consultants[@]} consultants..."
|
|
244
|
+
|
|
245
|
+
for consultant in "${consultants[@]}"; do
|
|
246
|
+
local consultant_lower
|
|
247
|
+
consultant_lower=$(to_lower "$consultant")
|
|
248
|
+
local response_file="$RESPONSES_DIR/${consultant_lower}.json"
|
|
249
|
+
|
|
250
|
+
# Skip if no previous round response
|
|
251
|
+
if [[ ! -f "$response_file" || ! -s "$response_file" ]]; then
|
|
252
|
+
log_warn "$consultant: no previous response, skipping"
|
|
253
|
+
continue
|
|
254
|
+
fi
|
|
255
|
+
|
|
256
|
+
local output_file="$OUTPUT_DIR/${consultant_lower}.json"
|
|
257
|
+
output_files+=("$output_file")
|
|
258
|
+
|
|
259
|
+
# Build debate prompt
|
|
260
|
+
local debate_prompt=$(build_debate_prompt "$consultant" "$response_file" "$RESPONSES_DIR")
|
|
261
|
+
|
|
262
|
+
# Execute query in background
|
|
263
|
+
(
|
|
264
|
+
local start_time
|
|
265
|
+
start_time=$(get_timestamp_ms)
|
|
266
|
+
|
|
267
|
+
# Route to appropriate query script based on consultant
|
|
268
|
+
case "$consultant" in
|
|
269
|
+
Gemini)
|
|
270
|
+
echo "$debate_prompt" | "$SCRIPT_DIR/query_gemini.sh" "" "" "$output_file" > /dev/null
|
|
271
|
+
;;
|
|
272
|
+
Codex)
|
|
273
|
+
"$SCRIPT_DIR/query_codex.sh" "$debate_prompt" "" "$output_file" > /dev/null
|
|
274
|
+
;;
|
|
275
|
+
Mistral)
|
|
276
|
+
"$SCRIPT_DIR/query_mistral.sh" "$debate_prompt" "" "$output_file" > /dev/null
|
|
277
|
+
;;
|
|
278
|
+
Kilo)
|
|
279
|
+
"$SCRIPT_DIR/query_kilo.sh" "$debate_prompt" "" "$output_file" > /dev/null
|
|
280
|
+
;;
|
|
281
|
+
Cursor)
|
|
282
|
+
"$SCRIPT_DIR/query_cursor.sh" "$debate_prompt" "" "$output_file" > /dev/null
|
|
283
|
+
;;
|
|
284
|
+
Aider)
|
|
285
|
+
"$SCRIPT_DIR/query_aider.sh" "$debate_prompt" "" "$output_file" > /dev/null
|
|
286
|
+
;;
|
|
287
|
+
Claude)
|
|
288
|
+
"$SCRIPT_DIR/query_claude.sh" "$debate_prompt" "" "$output_file" > /dev/null
|
|
289
|
+
;;
|
|
290
|
+
Qwen3)
|
|
291
|
+
"$SCRIPT_DIR/query_qwen3.sh" "$debate_prompt" "" "$output_file" > /dev/null
|
|
292
|
+
;;
|
|
293
|
+
GLM)
|
|
294
|
+
"$SCRIPT_DIR/query_glm.sh" "$debate_prompt" "" "$output_file" > /dev/null
|
|
295
|
+
;;
|
|
296
|
+
Grok)
|
|
297
|
+
"$SCRIPT_DIR/query_grok.sh" "$debate_prompt" "" "$output_file" > /dev/null
|
|
298
|
+
;;
|
|
299
|
+
DeepSeek)
|
|
300
|
+
"$SCRIPT_DIR/query_deepseek.sh" "$debate_prompt" "" "$output_file" > /dev/null
|
|
301
|
+
;;
|
|
302
|
+
Ollama)
|
|
303
|
+
"$SCRIPT_DIR/query_ollama.sh" "$debate_prompt" "" "$output_file" > /dev/null
|
|
304
|
+
;;
|
|
305
|
+
Amp)
|
|
306
|
+
"$SCRIPT_DIR/query_amp.sh" "$debate_prompt" "" "$output_file" > /dev/null
|
|
307
|
+
;;
|
|
308
|
+
Kimi)
|
|
309
|
+
"$SCRIPT_DIR/query_kimi.sh" "$debate_prompt" "" "$output_file" > /dev/null
|
|
310
|
+
;;
|
|
311
|
+
MiniMax)
|
|
312
|
+
"$SCRIPT_DIR/query_minimax.sh" "$debate_prompt" "" "$output_file" > /dev/null
|
|
313
|
+
;;
|
|
314
|
+
*)
|
|
315
|
+
log_error "Unknown consultant in debate round: $consultant (no query script mapped)"
|
|
316
|
+
exit 1
|
|
317
|
+
;;
|
|
318
|
+
esac
|
|
319
|
+
|
|
320
|
+
local end_time
|
|
321
|
+
end_time=$(get_timestamp_ms)
|
|
322
|
+
local latency=$((end_time - start_time))
|
|
323
|
+
|
|
324
|
+
# Add debate metadata to result
|
|
325
|
+
if [[ -f "$output_file" && -s "$output_file" ]]; then
|
|
326
|
+
local temp_file=$(mktemp)
|
|
327
|
+
if jq --argjson round "$ROUND_NUMBER" \
|
|
328
|
+
--argjson latency "$latency" \
|
|
329
|
+
'. + {debate_round: $round, debate_latency_ms: $latency}' \
|
|
330
|
+
"$output_file" > "$temp_file"; then
|
|
331
|
+
mv "$temp_file" "$output_file"
|
|
332
|
+
else
|
|
333
|
+
log_warn "Failed to add debate metadata for $consultant"
|
|
334
|
+
rm -f "$temp_file"
|
|
335
|
+
fi
|
|
336
|
+
fi
|
|
337
|
+
) 2>"$OUTPUT_DIR/${consultant_lower}.err" &
|
|
338
|
+
|
|
339
|
+
pids+=($!)
|
|
340
|
+
consultant_names+=("$consultant")
|
|
341
|
+
done
|
|
342
|
+
|
|
343
|
+
# Wait for all jobs
|
|
344
|
+
local success_count=0
|
|
345
|
+
for i in "${!pids[@]}"; do
|
|
346
|
+
local cname="${consultant_names[$i]}"
|
|
347
|
+
local cname_lower
|
|
348
|
+
cname_lower=$(to_lower "$cname")
|
|
349
|
+
local err_file="$OUTPUT_DIR/${cname_lower}.err"
|
|
350
|
+
if wait "${pids[$i]}" 2>/dev/null; then
|
|
351
|
+
((success_count++)) || true
|
|
352
|
+
else
|
|
353
|
+
log_warn "Debate round $ROUND_NUMBER: $cname failed"
|
|
354
|
+
if [[ -s "$err_file" ]]; then
|
|
355
|
+
log_debug " Error details: $(head -c 200 "$err_file")"
|
|
356
|
+
fi
|
|
357
|
+
fi
|
|
358
|
+
# Clean up empty error logs
|
|
359
|
+
[[ -f "$err_file" && ! -s "$err_file" ]] && rm -f "$err_file"
|
|
360
|
+
done
|
|
361
|
+
|
|
362
|
+
log_success "Debate round $ROUND_NUMBER completed: $success_count/${#pids[@]} responses"
|
|
363
|
+
|
|
364
|
+
# Generate round summary
|
|
365
|
+
generate_round_summary
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
# Generate debate round summary
|
|
369
|
+
generate_round_summary() {
|
|
370
|
+
local summary_file="$OUTPUT_DIR/round_summary.json"
|
|
371
|
+
|
|
372
|
+
local position_changes=0
|
|
373
|
+
local total_critiques=0
|
|
374
|
+
local consultants_responded=0
|
|
375
|
+
|
|
376
|
+
for f in "$OUTPUT_DIR"/*.json; do
|
|
377
|
+
if [[ -f "$f" && "$f" != *"summary"* ]]; then
|
|
378
|
+
((consultants_responded++)) || true
|
|
379
|
+
|
|
380
|
+
local changed=$(jq -r '.debate.position_changed // false' "$f" 2>/dev/null)
|
|
381
|
+
if [[ "$changed" == "true" ]]; then
|
|
382
|
+
((position_changes++)) || true
|
|
383
|
+
fi
|
|
384
|
+
|
|
385
|
+
local critiques=$(jq -r '.debate.critiques | length // 0' "$f" 2>/dev/null)
|
|
386
|
+
total_critiques=$((total_critiques + critiques))
|
|
387
|
+
fi
|
|
388
|
+
done
|
|
389
|
+
|
|
390
|
+
jq -n \
|
|
391
|
+
--argjson round "$ROUND_NUMBER" \
|
|
392
|
+
--argjson responded "$consultants_responded" \
|
|
393
|
+
--argjson changes "$position_changes" \
|
|
394
|
+
--argjson critiques "$total_critiques" \
|
|
395
|
+
--arg timestamp "$(date -Iseconds)" \
|
|
396
|
+
'{
|
|
397
|
+
round: $round,
|
|
398
|
+
timestamp: $timestamp,
|
|
399
|
+
consultants_responded: $responded,
|
|
400
|
+
position_changes: $changes,
|
|
401
|
+
total_critiques: $critiques,
|
|
402
|
+
stability: (if $changes == 0 then "stable" elif $changes <= 1 then "mostly_stable" else "volatile" end)
|
|
403
|
+
}' > "$summary_file"
|
|
404
|
+
|
|
405
|
+
log_info "Round summary saved: $summary_file"
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
# =============================================================================
|
|
409
|
+
# ENTRY POINT
|
|
410
|
+
# =============================================================================
|
|
411
|
+
|
|
412
|
+
# Check if debate should be skipped based on confidence spread (v2.3)
|
|
413
|
+
if [[ "$ROUND_NUMBER" == "2" ]] && should_skip_debate "$RESPONSES_DIR" "$QUESTION_CATEGORY"; then
|
|
414
|
+
log_info "Debate skipped: confidence spread below threshold (all consultants agree)"
|
|
415
|
+
echo '{"skipped": true, "reason": "confidence_spread_below_threshold"}' > "$OUTPUT_DIR/round_summary.json"
|
|
416
|
+
echo "$OUTPUT_DIR"
|
|
417
|
+
exit 0
|
|
418
|
+
fi
|
|
419
|
+
|
|
420
|
+
run_debate_round
|
|
421
|
+
echo "$OUTPUT_DIR"
|