-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaidd.sh
More file actions
593 lines (511 loc) · 24.3 KB
/
aidd.sh
File metadata and controls
593 lines (511 loc) · 24.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
#!/usr/bin/env bash
set -euo pipefail
# =============================================================================
# aidd.sh - AI Development Driver
# =============================================================================
# This script orchestrates AI-driven development using multiple supported CLIs.
#
# Module Structure:
# - lib/config.sh: Configuration constants and defaults
# - lib/utils.sh: Utility functions (logging, file operations)
# - lib/args.sh: Command-line argument parsing
# - lib/cli-factory.sh: CLI abstraction factory
# - lib/cli-opencode.sh: OpenCode CLI interaction functions
# - lib/cli-kilocode.sh: KiloCode CLI interaction functions
# - lib/cli-claude-code.sh: Claude Code CLI interaction functions
# - lib/cli-codex.sh: Codex CLI interaction functions
# - lib/cli-zrun.sh: ZRun CLI interaction functions
# - lib/project.sh: Project initialization and management
# - lib/iteration.sh: Iteration handling and state management
# =============================================================================
# -----------------------------------------------------------------------------
# Source Library Modules
# -----------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/lib/config.sh"
source "${SCRIPT_DIR}/lib/utils.sh"
source "${SCRIPT_DIR}/lib/log-cleaner.sh"
source "${SCRIPT_DIR}/lib/log-extractor.sh"
source "${SCRIPT_DIR}/lib/args.sh"
# -----------------------------------------------------------------------------
# System Compatibility Checks
# -----------------------------------------------------------------------------
if ! check_system_compatibility; then
echo "ERROR: System compatibility checks failed. Please see errors above." >&2
exit $EXIT_VALIDATION_ERROR
fi
# ---------------------------------------------------------------------------
# ARGUMENT PARSING
# ---------------------------------------------------------------------------
init_args "$@"
# ---------------------------------------------------------------------------
# HANDLE --stop FLAG (early exit, no CLI needed)
# ---------------------------------------------------------------------------
if [[ "$STOP_SIGNAL" == true ]]; then
if [[ -z "$PROJECT_DIR" ]]; then
echo "Error: --stop requires --project-dir" >&2
exit $EXIT_INVALID_ARGS
fi
stop_file="$PROJECT_DIR/.automaker/.stop"
mkdir -p "$(dirname "$stop_file")" 2>/dev/null
echo "$(date -Is 2>/dev/null || date)" > "$stop_file"
echo "Stop signal created: $stop_file"
echo "Running AIDD instance will stop after current iteration completes."
exit $EXIT_SUCCESS
fi
# ---------------------------------------------------------------------------
# CLI INITIALIZATION
# ---------------------------------------------------------------------------
# Initialize CLI based on the --cli parameter
source "${SCRIPT_DIR}/lib/cli-factory.sh"
if ! init_cli "$CLI_TYPE"; then
log_error "Failed to initialize CLI: $CLI_TYPE"
exit $EXIT_CLI_ERROR
fi
# Check if CLI is available
if ! check_cli_available; then
log_error "$CLI_NAME is not available. Please install it first."
exit $EXIT_CLI_ERROR
fi
# Log CLI version information
CLI_VERSION=$(get_cli_version)
if [[ -n "$CLI_VERSION" && "$CLI_VERSION" != "unknown" && "$CLI_VERSION" != "not installed" ]]; then
log_info "Using CLI: $CLI_NAME ($CLI_TYPE) version $CLI_VERSION"
else
log_info "Using CLI: $CLI_NAME ($CLI_TYPE)"
log_warn "Unable to determine $CLI_NAME version"
fi
# Source remaining modules that depend on CLI being initialized
source "${SCRIPT_DIR}/lib/project.sh"
source "${SCRIPT_DIR}/lib/iteration.sh"
# ---------------------------------------------------------------------------
# INITIALIZATION
# ---------------------------------------------------------------------------
# Find or create metadata directory
METADATA_DIR=$(find_or_create_metadata_dir "$PROJECT_DIR")
# Check if spec is required (only for new projects or when metadata dir doesn't have app_spec.txt)
# Audit mode skips spec requirement - audits run against existing codebases
NEEDS_SPEC=false
if [[ "$AUDIT_MODE" != "true" && "$INTERVIEW_MODE" != "true" ]]; then
if [[ ! -d "$PROJECT_DIR" ]] || ! is_existing_codebase "$PROJECT_DIR"; then
NEEDS_SPEC=true
fi
fi
if [[ "$NEEDS_SPEC" == true && -z "$SPEC_FILE" ]]; then
log_error "Missing required argument --spec (required for new projects or when app_spec.txt doesn't exist)"
log_info "Use --help for usage information"
exit $EXIT_INVALID_ARGS
fi
# Ensure project directory exists (create if missing)
if [[ ! -d "$PROJECT_DIR" ]]; then
log_info "Project directory '$PROJECT_DIR' does not exist; creating it..."
mkdir -p "$PROJECT_DIR"
NEW_PROJECT_CREATED=true
# Copy scaffolding files to the new project directory (including hidden files)
log_info "Copying scaffolding files to '$PROJECT_DIR'..."
if [[ -d "$SCRIPT_DIR/$DEFAULT_SCAFFOLDING_DIR" ]]; then
for item in "$SCRIPT_DIR/$DEFAULT_SCAFFOLDING_DIR"/*; do
if [[ -e "$item" ]]; then
local basename=$(basename "$item")
if safe_copy "$item" "$PROJECT_DIR/$basename" "$PROJECT_DIR"; then
log_debug "Copied scaffolding: $basename"
else
log_warn "Failed to copy scaffolding: $basename"
fi
fi
done
fi
# Copy artifacts contents to project's metadata folder
log_info "Copying templates to '$METADATA_DIR'..."
mkdir -p "$METADATA_DIR" && chmod 755 "$METADATA_DIR"
if [[ -d "$SCRIPT_DIR/$DEFAULT_TEMPLATES_DIR" ]]; then
for item in "$SCRIPT_DIR/$DEFAULT_TEMPLATES_DIR"/*; do
if [[ -e "$item" ]]; then
local basename=$(basename "$item")
if safe_copy "$item" "$METADATA_DIR/$basename" "$PROJECT_DIR"; then
log_debug "Copied artifact: $basename"
else
log_warn "Failed to copy artifact: $basename"
fi
fi
done
fi
else
# Check if this is an existing codebase
if is_existing_codebase "$PROJECT_DIR"; then
log_info "Detected existing codebase in '$PROJECT_DIR'"
fi
fi
# Check if spec file exists (only if provided)
if [[ -n "$SPEC_FILE" && ! -f "$SPEC_FILE" ]]; then
log_error "Spec file '$SPEC_FILE' does not exist"
exit $EXIT_NOT_FOUND
fi
# Define the paths to check
SPEC_CHECK_PATH="$METADATA_DIR/$DEFAULT_SPEC_FILE"
# Create iterations directory for transcript logs
ITERATIONS_DIR_FULL="$METADATA_DIR/$DEFAULT_ITERATIONS_DIR"
mkdir -p "$ITERATIONS_DIR_FULL"
# Initialize log index
NEXT_LOG_INDEX="$(get_next_log_index "$ITERATIONS_DIR_FULL")"
# Check onboarding status (diagnostic - don't exit on incomplete)
check_onboarding_status "$METADATA_DIR" || true
# Initialize failure counter
CONSECUTIVE_FAILURES=0
# Track whether remediation coding is active (break instead of exit on completion)
REMEDIATION_ACTIVE=false
log_info "Project directory: $PROJECT_DIR"
log_info "CLI: $CLI_NAME"
log_info "Post-completion audits configured: ${AUDIT_ON_COMPLETION_NAMES[*]} (${#AUDIT_ON_COMPLETION_NAMES[@]} audits)"
# ---------------------------------------------------------------------------
# EXIT HANDLERS
# ---------------------------------------------------------------------------
# Function to clean logs on exit
cleanup_logs() {
if [[ "$NO_CLEAN" == true ]]; then
log_info "Skipping log cleanup (--no-clean flag set)"
return
fi
# Use native bash log cleaning (no Node.js required)
cleanup_iteration_logs "$ITERATIONS_DIR_FULL" --no-backup
}
# Function to handle script exit with proper exit codes
handle_script_exit() {
local exit_code=$?
# Clean logs first (unless --no-clean)
cleanup_logs
case $exit_code in
$EXIT_SUCCESS) return ;; # Success, no message needed
$EXIT_NO_ASSISTANT) return ;; # No assistant messages
$EXIT_IDLE_TIMEOUT) return ;; # Idle timeout
$EXIT_PROVIDER_ERROR) return ;; # Provider error
$EXIT_RATE_LIMITED) return ;; # Rate limited (handled by retry loop)
$EXIT_SIGNAL_TERMINATED)
# Don't log error here - handle_failure() already logged whether we're aborting or continuing
# This trap only runs on final script exit, not on per-iteration timeout
return 1
;;
130)
log_error "Invalid configuration or system failure (exit=130)"
return 1
;;
*)
log_error "Unknown exit code from CLI (exit=$exit_code)"
return 1
;;
esac
}
# Set trap to handle exit codes and clean logs on script exit
trap handle_script_exit EXIT
# ---------------------------------------------------------------------------
# MAIN EXECUTION LOOP
# ---------------------------------------------------------------------------
log_info "Starting AI development driver with $CLI_NAME"
# Clean up stale stop file from previous run (prevents accidental immediate exit)
if [[ -f "$METADATA_DIR/.stop" ]]; then
log_warn "Removing stale .stop file from previous run"
rm -f "$METADATA_DIR/.stop" 2>/dev/null
fi
# Capture session start time so should_stop_audit_mode can distinguish
# reports written by THIS orchestrator run from stale reports left over
# from earlier runs on the same calendar day. Without this, re-running
# audits later the same day silently no-ops because today's report files
# still exist on disk and the pre-dispatch idempotency check short-circuits
# the iteration before the CLI is ever invoked.
AIDD_SESSION_START_EPOCH=$(date +%s)
export AIDD_SESSION_START_EPOCH
# ---------------------------------------------------------------------------
# Multi-Audit Loop (runs once if not in audit mode or single audit)
# ---------------------------------------------------------------------------
run_single_audit_or_all() {
local current_audit="$1"
local audit_num="$2"
local total_audits="$3"
# Set AUDIT_NAME for this iteration
if [[ -n "$current_audit" ]]; then
AUDIT_NAME="$current_audit"
export AUDIT_NAME
if [[ $total_audits -gt 1 ]]; then
log_header "AUDIT $audit_num of $total_audits: $AUDIT_NAME"
fi
fi
# Unified iteration loop — handles both unlimited and fixed modes
i=1
local consecutive_no_change=0
local MAX_NO_CHANGE_ITERATIONS=3
local consecutive_idle_timeouts=0
local MAX_CONSECUTIVE_IDLE_TIMEOUTS=2
if [[ -z "$MAX_ITERATIONS" ]]; then
log_info "Running unlimited iterations (use Ctrl+C to stop)"
else
log_info "Running $MAX_ITERATIONS iterations"
fi
while true; do
# Break when fixed iteration limit is reached
if [[ -n "$MAX_ITERATIONS" ]] && (( i > MAX_ITERATIONS )); then
break
fi
printf -v LOG_FILE "%s/%03d.log" "$ITERATIONS_DIR_FULL" "$NEXT_LOG_INDEX"
NEXT_LOG_INDEX=$((NEXT_LOG_INDEX + 1))
# Stuck detection: capture git state before iteration (unlimited only)
# Exclude .automaker/ metadata — formatting drift shouldn't reset stuck counter
# Skip for interview mode — it only writes to .automaker/responses/ by design
local pre_iteration_head=""
local pre_iteration_dirty=""
if [[ -z "$MAX_ITERATIONS" && "$INTERVIEW_MODE" != "true" ]]; then
if command -v git >/dev/null 2>&1 && git -C "$PROJECT_DIR" rev-parse --git-dir >/dev/null 2>&1; then
pre_iteration_head=$(git -C "$PROJECT_DIR" rev-parse HEAD 2>/dev/null || echo "")
pre_iteration_dirty=$(git -C "$PROJECT_DIR" status --porcelain 2>/dev/null | grep -v ' .automaker/' || echo "")
fi
fi
{
# Determine which prompt to use based on project state
PROMPT_INFO=$(determine_prompt "$PROJECT_DIR" "$SCRIPT_DIR" "$METADATA_DIR") || {
log_error "Failed to determine prompt"
exit $EXIT_GENERAL_ERROR
}
PROMPT_PATH="${PROMPT_INFO%|*}"
PROMPT_TYPE="${PROMPT_INFO#*|}"
# Print comprehensive iteration header
log_iteration_header "$i" "${MAX_ITERATIONS:-}" "$LOG_FILE"
# Copy shared directories from copydirs.txt
copy_shared_directories "$PROJECT_DIR" "$SCRIPT_DIR"
# Copy shared files from copyfiles.txt
copy_shared_files "$PROJECT_DIR" "$SCRIPT_DIR"
# Copy AIDD internal _common modules to .automaker/_common/
copy_common_modules "$PROJECT_DIR" "$SCRIPT_DIR"
# Validate that the prompt file exists
if [[ ! -f "$PROMPT_PATH" ]]; then
log_error "Prompt file does not exist: $PROMPT_PATH"
exit $EXIT_GENERAL_ERROR
fi
# Copy artifacts if needed (for onboarding/initializer prompts)
if [[ "$PROMPT_TYPE" != "coding" && "$PROMPT_TYPE" != "directive" && "$PROMPT_TYPE" != "audit" && "$PROMPT_TYPE" != "interview" ]]; then
copy_templates "$PROJECT_DIR" "$SCRIPT_DIR"
fi
# Copy spec file if this is a new project with spec
if [[ "$PROMPT_TYPE" == "initializer" && -n "$SPEC_FILE" ]]; then
cp "$SPEC_FILE" "$SPEC_CHECK_PATH"
fi
# Generate status report BEFORE sending prompt (best-effort, non-fatal)
# This ensures we always have up-to-date status regardless of agent exit code
log_info "Generating project status before iteration..."
STATUS_FILE="$METADATA_DIR/status.md"
mkdir -p "$(dirname "$STATUS_FILE")" 2>/dev/null
{
show_status "$PROJECT_DIR"
} > "$STATUS_FILE" 2>&1 || true
log_info "Project status saved to: $STATUS_FILE"
# Check if current mode should stop early (TODO/in-progress with no items)
# This prevents unnecessary agent invocations when mode-specific work is done
if should_stop_current_mode "$METADATA_DIR"; then
log_info "Mode-specific work complete. Stopping early due to --stop-when-done flag."
exit $EXIT_SUCCESS
fi
# Run the appropriate prompt
prompt_name=$(basename "$PROMPT_PATH" .md)
local cli_start_time=$SECONDS
log_info "Sending $prompt_name prompt to $CLI_NAME... [$(date '+%H:%M:%S')]"
CLI_EXIT_CODE=0
if [[ "$PROMPT_TYPE" == "audit" ]]; then
run_cli_prompt "$PROJECT_DIR" "$PROMPT_PATH" "${AUDIT_MODEL_ARGS[@]}" || CLI_EXIT_CODE=$?
elif [[ "$PROMPT_TYPE" == "coding" || "$PROMPT_TYPE" == "directive" ]]; then
run_cli_prompt "$PROJECT_DIR" "$PROMPT_PATH" "${CODE_MODEL_ARGS[@]}" || CLI_EXIT_CODE=$?
else
run_cli_prompt "$PROJECT_DIR" "$PROMPT_PATH" "${INIT_MODEL_ARGS[@]}" || CLI_EXIT_CODE=$?
fi
local cli_elapsed=$((SECONDS - cli_start_time))
log_info "$CLI_NAME finished (exit=$CLI_EXIT_CODE) after $(format_elapsed $cli_elapsed) [$(date '+%H:%M:%S')]"
# Clean up directive.md after directive mode completes
if [[ "$PROMPT_TYPE" == "directive" ]]; then
rm -f "$METADATA_DIR/directive.md" 2>/dev/null
log_debug "Removed directive.md"
fi
# Clean up audit-prompt.md after audit mode completes
if [[ "$PROMPT_TYPE" == "audit" ]]; then
rm -f "$METADATA_DIR/audit-prompt.md" 2>/dev/null
log_debug "Removed audit-prompt.md"
fi
# Clean up interview-prompt.md and update index after interview iteration
if [[ "$PROMPT_TYPE" == "interview" ]]; then
rm -f "$METADATA_DIR/interview-prompt.md" 2>/dev/null
log_debug "Removed interview-prompt.md"
update_interview_index "$METADATA_DIR" "$INTERVIEW_FILE"
fi
# Save CLI exit code to file so parent shell can read it
# (variables set inside { ... } | tee subshell don't propagate)
echo "$CLI_EXIT_CODE" > "$LOG_FILE.exitcode"
log_info "--- End of iteration $i ---"
log_info "Finished: $(date -Is 2>/dev/null || date)"
echo
} 2>&1 | tee "$LOG_FILE"
# Read CLI exit code from file (subshell variables don't propagate through pipe)
CLI_EXIT_CODE=$(cat "$LOG_FILE.exitcode" 2>/dev/null || echo "0")
rm -f "$LOG_FILE.exitcode" 2>/dev/null
# Track consecutive idle timeouts — if the agent stalls repeatedly
# on the same codebase state, continuing is unlikely to help
if [[ $CLI_EXIT_CODE -eq $EXIT_IDLE_TIMEOUT ]]; then
consecutive_idle_timeouts=$((consecutive_idle_timeouts + 1))
log_warn "Idle timeout #$consecutive_idle_timeouts/$MAX_CONSECUTIVE_IDLE_TIMEOUTS"
if [[ $consecutive_idle_timeouts -ge $MAX_CONSECUTIVE_IDLE_TIMEOUTS ]]; then
log_error "Stopped: $MAX_CONSECUTIVE_IDLE_TIMEOUTS consecutive idle timeouts. Agent appears stuck in analysis loop."
break
fi
else
consecutive_idle_timeouts=0
fi
# Handle rate limiting: sleep until reset, rewind log index and retry same iteration
if [[ $CLI_EXIT_CODE -eq $EXIT_RATE_LIMITED ]]; then
handle_rate_limit "$LOG_FILE"
rm -f "$LOG_FILE" 2>/dev/null
NEXT_LOG_INDEX=$((NEXT_LOG_INDEX - 1))
continue
fi
# Handle failure or reset counter based on CLI exit code
if [[ $CLI_EXIT_CODE -ne 0 ]]; then
# Special handling for exit code 1 (no more work) - don't rewind iteration
if [[ $CLI_EXIT_CODE -eq 1 && ("$CLI_TYPE" == "kilocode" || "$CLI_TYPE" == "opencode") ]]; then
log_info "$CLI_NAME completed with exit=1 (no more work) - checking for completion..."
reset_failure_counter
# Fall through to complete the iteration (don't continue)
else
# handle_failure either absorbs the error (returns 0) or exits the script
handle_failure "$CLI_EXIT_CODE"
continue
fi
else
reset_failure_counter
fi
# Extract structured log if enabled
if [[ "$EXTRACT_STRUCTURED" == true ]]; then
extract_single_log "$LOG_FILE" "$METADATA_DIR"
fi
# Check if current mode should stop early (TODO/in-progress with no items)
# This prevents unnecessary next iterations when mode-specific work is done
if should_stop_current_mode "$METADATA_DIR"; then
log_info "Mode-specific work complete. Stopping early due to --stop-when-done flag."
return 0
fi
# Check for user-requested stop file (graceful stop after current iteration)
local stop_file="$METADATA_DIR/.stop"
if [[ -f "$stop_file" ]]; then
log_info "Stop requested via .stop file. Exiting gracefully after iteration $i."
rm -f "$stop_file" 2>/dev/null
exit $EXIT_ABORTED
fi
# Stuck detection: check if agent made meaningful changes (unlimited only)
# Exclude .automaker/ metadata files — formatting drift on status.md etc.
# should not count as "real" changes that reset the stuck counter
if [[ -z "$MAX_ITERATIONS" && "$INTERVIEW_MODE" != "true" ]]; then
local post_iteration_head=""
local post_iteration_dirty=""
if command -v git >/dev/null 2>&1 && git -C "$PROJECT_DIR" rev-parse --git-dir >/dev/null 2>&1; then
post_iteration_head=$(git -C "$PROJECT_DIR" rev-parse HEAD 2>/dev/null || echo "")
post_iteration_dirty=$(git -C "$PROJECT_DIR" status --porcelain 2>/dev/null | grep -v ' .automaker/' || echo "")
fi
if [[ "$pre_iteration_head" == "$post_iteration_head" && "$pre_iteration_dirty" == "$post_iteration_dirty" ]]; then
consecutive_no_change=$((consecutive_no_change + 1))
log_warn "No meaningful changes detected in iteration $i ($consecutive_no_change/$MAX_NO_CHANGE_ITERATIONS consecutive)"
if [[ $consecutive_no_change -ge $MAX_NO_CHANGE_ITERATIONS ]]; then
log_error "Stopped: $MAX_NO_CHANGE_ITERATIONS consecutive iterations with no meaningful changes."
break
fi
else
consecutive_no_change=0
fi
fi
i=$((i + 1))
done
log_info "Audit iterations completed for: $AUDIT_NAME"
}
# ---------------------------------------------------------------------------
# Helper: Run a set of audits sequentially
# ---------------------------------------------------------------------------
run_audit_set() {
local -a audit_set=("$@")
local total=${#audit_set[@]}
if [[ $total -gt 1 ]]; then
log_info "Running $total audits sequentially: ${audit_set[*]}"
for ((idx=0; idx<total; idx++)); do
AUDIT_NAME="${audit_set[$idx]}"
AUDIT_INDEX=$idx
export AUDIT_NAME AUDIT_INDEX
run_single_audit_or_all "$AUDIT_NAME" "$((idx + 1))" "$total"
rm -f "$METADATA_DIR/audit-prompt.md" 2>/dev/null
if [[ $idx -lt $((total - 1)) ]]; then
log_info ""
log_info "Proceeding to next audit..."
log_info ""
fi
done
log_info "All $total audits completed"
else
AUDIT_NAME="${audit_set[0]}"
export AUDIT_NAME
run_single_audit_or_all "$AUDIT_NAME" "1" "1"
fi
}
# ---------------------------------------------------------------------------
# Execute Main Run
# ---------------------------------------------------------------------------
if [[ "$AUDIT_MODE" == "true" ]]; then
run_audit_set "${AUDIT_NAMES[@]}"
else
# Non-audit mode (coding, todo, validate, etc.)
run_single_audit_or_all "$AUDIT_NAME" "1" "1"
log_info "AI development driver completed successfully"
fi
# ---------------------------------------------------------------------------
# Post-Completion Audits (--audit-on-completion)
# ---------------------------------------------------------------------------
# Run post-completion audits if configured and project is complete
if [[ "$AUDIT_MODE" != "true" && ${#AUDIT_ON_COMPLETION_NAMES[@]} -gt 0 ]]; then
log_info "Checking project completion for post-completion audits..."
if check_project_completion "$METADATA_DIR"; then
log_header "POST-COMPLETION AUDITS: ${AUDIT_ON_COMPLETION_NAMES[*]}"
AUDIT_MODE=true
export AUDIT_MODE
run_audit_set "${AUDIT_ON_COMPLETION_NAMES[@]}"
log_info "Post-completion audits finished"
else
log_info "Project not complete, skipping post-completion audits"
fi
fi
# ---------------------------------------------------------------------------
# Audit Remediation Loop (--code-after-audit)
# ---------------------------------------------------------------------------
if [[ "$CODE_AFTER_AUDIT" == "true" ]]; then
MAX_REMEDIATION_CYCLES=10
remediation_cycle=0
while true; do
unfixed=$(count_unfixed_audit_findings "$METADATA_DIR")
if [[ "$unfixed" -eq 0 ]]; then
log_info "No unfixed audit findings. Audit remediation complete."
break
fi
remediation_cycle=$((remediation_cycle + 1))
if [[ $remediation_cycle -gt $MAX_REMEDIATION_CYCLES ]]; then
log_warn "Maximum remediation cycles ($MAX_REMEDIATION_CYCLES) reached. $unfixed findings remain."
break
fi
log_header "REMEDIATION CYCLE $remediation_cycle ($unfixed findings)"
# Run coding iterations to fix audit findings
AUDIT_MODE=false
AUDIT_NAME=""
REMEDIATION_ACTIVE=true
export AUDIT_MODE AUDIT_NAME
run_single_audit_or_all "" "1" "1"
REMEDIATION_ACTIVE=false
# Re-run audits to check for remaining/new findings
log_header "RE-AUDIT (cycle $remediation_cycle)"
AUDIT_MODE=true
export AUDIT_MODE
# Re-run whichever audit set was originally configured
if [[ ${#AUDIT_ON_COMPLETION_NAMES[@]} -gt 0 ]]; then
run_audit_set "${AUDIT_ON_COMPLETION_NAMES[@]}"
else
run_audit_set "${AUDIT_NAMES[@]}"
fi
done
fi
exit $EXIT_SUCCESS